code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_send_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 TestAdminSendTransaction(t *testing.T) { 39 t.Run("Documentation matches the code", testAdminSendTransactionSchemaCorrect) 40 t.Run("Sending transaction with invalid params fails", testAdminSendingTransactionWithInvalidParamsFails) 41 t.Run("Sending transaction with valid params succeeds", testAdminSendingTransactionWithValidParamsSucceeds) 42 t.Run("Getting internal error during wallet verification fails", testAdminSendTransactionGettingInternalErrorDuringWalletVerificationFails) 43 t.Run("Sending transaction with wallet that doesn't exist fails", testAdminSendingTransactionWithWalletThatDoesntExistFails) 44 t.Run("Getting internal error during wallet retrieval fails", testAdminSendTransactionGettingInternalErrorDuringWalletRetrievalFails) 45 t.Run("Sending transaction with malformed transaction fails", testAdminSendingTransactionWithMalformedTransactionFails) 46 t.Run("Sending transaction which is invalid fails", testAdminSendingTransactionWithInvalidTransactionFails) 47 } 48 49 func testAdminSendTransactionSchemaCorrect(t *testing.T) { 50 assertEqualSchema(t, "admin.send_transaction", api.AdminSendTransactionParams{}, api.AdminSendTransactionResult{}) 51 } 52 53 func testAdminSendingTransactionWithInvalidParamsFails(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.AdminSendTransactionParams{ 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.AdminSendTransactionParams{ 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.AdminSendTransactionParams{ 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.AdminSendTransactionParams{ 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.AdminSendTransactionParams{ 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 := newAdminSendTransactionHandler(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 testAdminSendingTransactionWithValidParamsSucceeds(t *testing.T) { 141 // given 142 ctx := context.Background() 143 network := newNetwork(t) 144 nodeHost := vgrand.RandomStr(5) 145 w, kp := walletWithKey(t) 146 hash := "hashy mchashface" 147 148 // setup 149 handler := newAdminSendTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) { 150 ctrl := gomock.NewController(t) 151 nodeSelector := nodemocks.NewMockSelector(ctrl) 152 node := nodemocks.NewMockNode(ctrl) 153 nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil) 154 node.EXPECT().LastBlock(ctx).Times(1).Return(types.LastBlock{ 155 BlockHeight: 150, 156 BlockHash: vgrand.RandomStr(64), 157 ProofOfWorkHashFunction: vgcrypto.Sha3, 158 ProofOfWorkDifficulty: 1, 159 ChainID: vgrand.RandomStr(5), 160 }, nil) 161 node.EXPECT().SendTransaction(ctx, gomock.Any(), gomock.Any()).Times(1).Return(hash, nil) 162 node.EXPECT().Host().Times(1).Return(nodeHost) 163 return nodeSelector, nil 164 }) 165 166 // -- expected calls 167 handler.walletStore.EXPECT().WalletExists(ctx, w.Name()).Times(1).Return(true, nil) 168 handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, w.Name()).Times(1).Return(true, nil) 169 handler.walletStore.EXPECT().GetWallet(ctx, w.Name()).Times(1).Return(w, nil) 170 handler.networkStore.EXPECT().NetworkExists(network.Name).Times(1).Return(true, nil) 171 handler.networkStore.EXPECT().GetNetwork(network.Name).Times(1).Return(&network, nil) 172 173 // when 174 result, errorDetails := handler.handle(t, ctx, api.AdminSendTransactionParams{ 175 Wallet: w.Name(), 176 PublicKey: kp.PublicKey(), 177 Network: network.Name, 178 Transaction: testTransaction(t), 179 }) 180 181 // then 182 assert.Nil(t, errorDetails) 183 assert.Equal(t, hash, result.TransactionHash) 184 assert.NotEmpty(t, result.Transaction) 185 } 186 187 func testAdminSendTransactionGettingInternalErrorDuringWalletVerificationFails(t *testing.T) { 188 // given 189 ctx := context.Background() 190 network := newNetwork(t) 191 walletName := vgrand.RandomStr(5) 192 193 // setup 194 handler := newAdminSendTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) { 195 ctrl := gomock.NewController(t) 196 nodeSelector := nodemocks.NewMockSelector(ctrl) 197 node := nodemocks.NewMockNode(ctrl) 198 nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil) 199 node.EXPECT().LastBlock(ctx).Times(1).Return(types.LastBlock{ 200 BlockHeight: 150, 201 BlockHash: vgrand.RandomStr(64), 202 ProofOfWorkHashFunction: vgcrypto.Sha3, 203 ProofOfWorkDifficulty: 1, 204 ChainID: vgrand.RandomStr(5), 205 }, nil) 206 return nodeSelector, nil 207 }) 208 209 // -- expected calls 210 handler.walletStore.EXPECT().WalletExists(ctx, walletName).Times(1).Return(false, assert.AnError) 211 212 // when 213 result, errorDetails := handler.handle(t, ctx, api.AdminSendTransactionParams{ 214 Wallet: walletName, 215 PublicKey: vgrand.RandomStr(5), 216 Network: network.Name, 217 Transaction: testTransaction(t), 218 }) 219 220 // then 221 assertInternalError(t, errorDetails, fmt.Errorf("could not verify the wallet exists: %w", assert.AnError)) 222 assert.Empty(t, result) 223 } 224 225 func testAdminSendingTransactionWithWalletThatDoesntExistFails(t *testing.T) { 226 // given 227 ctx := context.Background() 228 229 params := api.AdminSendTransactionParams{ 230 Wallet: vgrand.RandomStr(5), 231 PublicKey: vgrand.RandomStr(5), 232 Network: "fairground", 233 Transaction: testTransaction(t), 234 } 235 236 // setup 237 handler := newAdminSendTransactionHandler(t, unexpectedNodeSelectorCall(t)) 238 239 // -- expected calls 240 handler.walletStore.EXPECT().WalletExists(ctx, params.Wallet).Times(1).Return(false, nil) 241 242 // when 243 result, errorDetails := handler.handle(t, ctx, params) 244 245 // then 246 assertInvalidParams(t, errorDetails, api.ErrWalletDoesNotExist) 247 assert.Empty(t, result) 248 } 249 250 func testAdminSendTransactionGettingInternalErrorDuringWalletRetrievalFails(t *testing.T) { 251 // given 252 ctx := context.Background() 253 network := newNetwork(t) 254 walletName := vgrand.RandomStr(5) 255 256 // setup 257 handler := newAdminSendTransactionHandler(t, func(hosts []string, _ uint64, _ time.Duration) (walletnode.Selector, error) { 258 ctrl := gomock.NewController(t) 259 nodeSelector := nodemocks.NewMockSelector(ctrl) 260 node := nodemocks.NewMockNode(ctrl) 261 nodeSelector.EXPECT().Node(ctx, gomock.Any()).Times(1).Return(node, nil) 262 node.EXPECT().LastBlock(ctx).Times(1).Return(types.LastBlock{ 263 BlockHeight: 150, 264 BlockHash: vgrand.RandomStr(64), 265 ProofOfWorkHashFunction: vgcrypto.Sha3, 266 ProofOfWorkDifficulty: 1, 267 ChainID: vgrand.RandomStr(5), 268 }, nil) 269 return nodeSelector, nil 270 }) 271 272 // -- expected calls 273 handler.walletStore.EXPECT().WalletExists(ctx, walletName).Times(1).Return(true, nil) 274 handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, walletName).Times(1).Return(true, nil) 275 handler.walletStore.EXPECT().GetWallet(ctx, walletName).Times(1).Return(nil, assert.AnError) 276 277 // when 278 result, errorDetails := handler.handle(t, ctx, api.AdminSendTransactionParams{ 279 Wallet: walletName, 280 PublicKey: vgrand.RandomStr(5), 281 Network: network.Name, 282 Transaction: testTransaction(t), 283 }) 284 285 // then 286 assertInternalError(t, errorDetails, fmt.Errorf("could not retrieve the wallet: %w", assert.AnError)) 287 assert.Empty(t, result) 288 } 289 290 func testAdminSendingTransactionWithMalformedTransactionFails(t *testing.T) { 291 // given 292 ctx := context.Background() 293 network := vgrand.RandomStr(5) 294 w, kp := walletWithKey(t) 295 296 // setup 297 handler := newAdminSendTransactionHandler(t, unexpectedNodeSelectorCall(t)) 298 299 // -- expected calls 300 handler.walletStore.EXPECT().WalletExists(ctx, w.Name()).Times(1).Return(true, nil) 301 handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, w.Name()).Times(1).Return(true, nil) 302 handler.walletStore.EXPECT().GetWallet(ctx, w.Name()).Times(1).Return(w, nil) 303 304 // when 305 result, errorDetails := handler.handle(t, ctx, api.AdminSendTransactionParams{ 306 Wallet: w.Name(), 307 PublicKey: kp.PublicKey(), 308 Network: network, 309 Transaction: map[string]int{"bob": 5}, 310 }) 311 312 // then 313 assertInvalidParams(t, errorDetails, errors.New("the transaction does not use a valid Vega command: unknown field \"bob\" in vega.wallet.v1.SubmitTransactionRequest")) 314 assert.Empty(t, result) 315 } 316 317 func testAdminSendingTransactionWithInvalidTransactionFails(t *testing.T) { 318 // given 319 ctx := context.Background() 320 network := newNetwork(t) 321 w, kp := walletWithKey(t) 322 323 // setup 324 handler := newAdminSendTransactionHandler(t, unexpectedNodeSelectorCall(t)) 325 326 // -- expected calls 327 handler.walletStore.EXPECT().WalletExists(ctx, w.Name()).Times(1).Return(true, nil) 328 handler.walletStore.EXPECT().IsWalletAlreadyUnlocked(ctx, w.Name()).Times(1).Return(true, nil) 329 handler.walletStore.EXPECT().GetWallet(ctx, w.Name()).Times(1).Return(w, nil) 330 331 // when 332 result, errorDetails := handler.handle(t, ctx, api.AdminSendTransactionParams{ 333 Wallet: w.Name(), 334 PublicKey: kp.PublicKey(), 335 Network: network.Name, 336 Transaction: testMalformedTransaction(t), 337 }) 338 339 // then 340 assertInvalidParams(t, errorDetails, fmt.Errorf("vote_submission.proposal_id (should be a valid Vega ID)")) 341 assert.Empty(t, result) 342 } 343 344 type AdminSendTransactionHandler struct { 345 *api.AdminSendTransaction 346 ctrl *gomock.Controller 347 walletStore *mocks.MockWalletStore 348 networkStore *mocks.MockNetworkStore 349 } 350 351 func (h *AdminSendTransactionHandler) handle(t *testing.T, ctx context.Context, params jsonrpc.Params) (api.AdminSendTransactionResult, *jsonrpc.ErrorDetails) { 352 t.Helper() 353 354 rawResult, err := h.Handle(ctx, params) 355 if rawResult != nil { 356 result, ok := rawResult.(api.AdminSendTransactionResult) 357 if !ok { 358 t.Fatal("AdminUpdatePermissions handler result is not a AdminSendTransactionResult") 359 } 360 return result, err 361 } 362 return api.AdminSendTransactionResult{}, err 363 } 364 365 func newAdminSendTransactionHandler(t *testing.T, builder api.NodeSelectorBuilder) *AdminSendTransactionHandler { 366 t.Helper() 367 368 ctrl := gomock.NewController(t) 369 walletStore := mocks.NewMockWalletStore(ctrl) 370 networkStore := mocks.NewMockNetworkStore(ctrl) 371 372 return &AdminSendTransactionHandler{ 373 AdminSendTransaction: api.NewAdminSendTransaction(walletStore, networkStore, builder), 374 ctrl: ctrl, 375 walletStore: walletStore, 376 networkStore: networkStore, 377 } 378 }