code.vegaprotocol.io/vega@v0.79.0/wallet/api/api.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 17 18 import ( 19 "context" 20 "time" 21 22 "code.vegaprotocol.io/vega/libs/jsonrpc" 23 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 24 walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1" 25 "code.vegaprotocol.io/vega/wallet/api/node" 26 nodetypes "code.vegaprotocol.io/vega/wallet/api/node/types" 27 "code.vegaprotocol.io/vega/wallet/network" 28 "code.vegaprotocol.io/vega/wallet/wallet" 29 30 "go.uber.org/zap" 31 ) 32 33 // Generates mocks 34 //go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/wallet/api WalletStore,NetworkStore,Interactor,ConnectionsManager,SpamHandler 35 36 type NodeSelectorBuilder func(hosts []string, retries uint64, ttl time.Duration) (node.Selector, error) 37 38 // WalletStore is the component used to retrieve and update wallets from the 39 // computer. 40 type WalletStore interface { 41 UnlockWallet(ctx context.Context, name, passphrase string) error 42 LockWallet(ctx context.Context, name string) error 43 WalletExists(ctx context.Context, name string) (bool, error) 44 GetWallet(ctx context.Context, name string) (wallet.Wallet, error) 45 ListWallets(ctx context.Context) ([]string, error) 46 CreateWallet(ctx context.Context, w wallet.Wallet, passphrase string) error 47 UpdateWallet(ctx context.Context, w wallet.Wallet) error 48 UpdatePassphrase(ctx context.Context, name, newPassphrase string) error 49 DeleteWallet(ctx context.Context, name string) error 50 RenameWallet(ctx context.Context, currentName, newName string) error 51 GetWalletPath(name string) string 52 IsWalletAlreadyUnlocked(ctx context.Context, name string) (bool, error) 53 } 54 55 // NetworkStore is the component used to retrieve and update the networks from the 56 // computer. 57 type NetworkStore interface { 58 NetworkExists(string) (bool, error) 59 GetNetwork(string) (*network.Network, error) 60 SaveNetwork(*network.Network) error 61 ListNetworks() ([]string, error) 62 GetNetworkPath(string) string 63 DeleteNetwork(string) error 64 RenameNetwork(currentName, newName string) error 65 } 66 67 type ConnectionsManager interface { 68 EndSessionConnection(hostname, wallet string) 69 ListSessionConnections() []Connection 70 } 71 72 type SpamHandler interface { 73 GenerateProofOfWork(pubKey string, blockData *nodetypes.SpamStatistics) (*commandspb.ProofOfWork, error) 74 CheckSubmission(req *walletpb.SubmitTransactionRequest, latest *nodetypes.SpamStatistics) error 75 } 76 77 // Interactor is the component in charge of delegating the JSON-RPC API 78 // requests, notifications and logs to the wallet front-end. 79 // Convention: 80 // - Notify* calls do not expect a response from the user. 81 // - Request* calls are expecting a response from the user. 82 // - Log function is just information logging and does not expect a response. 83 // 84 //nolint:interfacebloat 85 type Interactor interface { 86 // NotifyInteractionSessionBegan notifies the beginning of an interaction 87 // session. 88 // A session is scoped to a request. 89 NotifyInteractionSessionBegan(ctx context.Context, traceID string, workflow WorkflowType, numberOfSteps uint8) error 90 91 // NotifyInteractionSessionEnded notifies the end of an interaction 92 // session. 93 // A session is scoped to a request. 94 NotifyInteractionSessionEnded(ctx context.Context, traceID string) 95 96 // NotifySuccessfulTransaction is used to report a successful transaction. 97 NotifySuccessfulTransaction(ctx context.Context, traceID string, stepNumber uint8, txHash, deserializedInputData, tx string, sentAt time.Time, host string) 98 99 // NotifyFailedTransaction is used to report a failed transaction. 100 NotifyFailedTransaction(ctx context.Context, traceID string, stepNumber uint8, deserializedInputData, tx string, err error, sentAt time.Time, host string) 101 102 // NotifySuccessfulRequest is used to notify the user the request is 103 // successful. 104 NotifySuccessfulRequest(ctx context.Context, traceID string, stepNumber uint8, message string) 105 106 // NotifyError is used to report errors to the user. 107 NotifyError(ctx context.Context, traceID string, t ErrorType, err error) 108 109 // Log is used to report information of any kind to the user. This is used 110 // to log internal activities and provide feedback to the wallet front-ends. 111 // It's purely for informational purpose. 112 // Receiving logs should be expected at any point during an interaction 113 // session. 114 Log(ctx context.Context, traceID string, t LogType, msg string) 115 116 // RequestWalletConnectionReview is used to trigger a user review of 117 // the wallet connection requested by the specified hostname. 118 // It returns the type of connection approval chosen by the user. 119 RequestWalletConnectionReview(ctx context.Context, traceID string, stepNumber uint8, hostname string) (string, error) 120 121 // RequestWalletSelection is used to trigger the selection of the wallet the 122 // user wants to use for the specified hostname. 123 RequestWalletSelection(ctx context.Context, traceID string, stepNumber uint8, hostname string, availableWallets []string) (string, error) 124 125 // RequestPassphrase is used to request to the user the passphrase of a wallet. 126 // It's primarily used by requests that update the wallet. 127 RequestPassphrase(ctx context.Context, traceID string, stepNumber uint8, wallet, reason string) (string, error) 128 129 // RequestPermissionsReview is used to trigger a user review of the permissions 130 // requested by the specified hostname. 131 // It returns true if the user approved the requested permissions, false 132 // otherwise. 133 RequestPermissionsReview(ctx context.Context, traceID string, stepNumber uint8, hostname, wallet string, perms map[string]string) (bool, error) 134 135 // RequestTransactionReviewForSending is used to trigger a user review of the 136 // transaction a third-party application wants to send. 137 // It returns true if the user approved the sending of the transaction, 138 // false otherwise. 139 RequestTransactionReviewForSending(ctx context.Context, traceID string, stepNumber uint8, hostname, wallet, pubKey, transaction string, receivedAt time.Time) (bool, error) 140 141 // RequestTransactionReviewForSigning is used to trigger a user review of the 142 // transaction a third-party application wants to sign. The wallet doesn't 143 // send the transaction. 144 // It returns true if the user approved the signing of the transaction, 145 // false otherwise. 146 RequestTransactionReviewForSigning(ctx context.Context, traceID string, stepNumber uint8, hostname, wallet, pubKey, transaction string, receivedAt time.Time) (bool, error) 147 148 // RequestTransactionReviewForChecking is used to prompt the user to check of the 149 // transaction a third-party application wants to receive. The wallet doesn't 150 // check the transaction. 151 // It returns true if the user approved the transaction, 152 // false otherwise. 153 RequestTransactionReviewForChecking(ctx context.Context, traceID string, stepNumber uint8, hostname, wallet, pubKey, transaction string, receivedAt time.Time) (bool, error) 154 } 155 156 // FileSchemePrefix defines the prefix used in URL's to indicate that the string represents a file-path. 157 const FileSchemePrefix = "file://" 158 159 // WorkflowType defines the type of interaction workflow that started by a 160 // method. 161 type WorkflowType string 162 163 var ( 164 WalletConnectionWorkflow WorkflowType = "WALLET_CONNECTION" 165 TransactionReviewWorkflow WorkflowType = "TRANSACTION_REVIEW" 166 PermissionRequestWorkflow WorkflowType = "PERMISSION_REQUEST" 167 WalletUnlockingWorkflow WorkflowType = "WALLET_UNLOCKING" 168 ) 169 170 // ErrorType defines the type of error that is sent to the user, for fine 171 // grain error management and reporting. 172 type ErrorType string 173 174 var ( 175 // InternalErrorType defines an unexpected technical error upon which the user 176 // can't act. 177 // The wallet front-end should report it to the user and automatically 178 // abort the processing of the ongoing request. 179 // It can be raised if a file is not accessible or corrupt, for example. 180 InternalErrorType ErrorType = "Internal error" 181 // ServerErrorType defines a programmatic error threw by the server, such as 182 // a request cancellation. The server error targets error that happens in 183 // the communication layer. It's different form application error. 184 // It's a type of error that should be expected and handled. 185 ServerErrorType ErrorType = "Server error" 186 // NetworkErrorType defines an error that comes from the network and its nodes. 187 NetworkErrorType ErrorType = "Network error" 188 // ApplicationErrorType defines a programmatic error threw by the application 189 // core, also called "business logic". 190 ApplicationErrorType ErrorType = "Application error" 191 // UserErrorType defines an error that originated from the user and that 192 // requires its intervention to correct it. 193 // It can be raised if a passphrase is invalid, for example. 194 UserErrorType ErrorType = "User error" 195 ) 196 197 // LogType defines the type of log that is sent to the user. 198 type LogType string 199 200 var ( 201 InfoLog LogType = "Info" 202 WarningLog LogType = "Warning" 203 ErrorLog LogType = "Error" 204 SuccessLog LogType = "Success" 205 ) 206 207 type ClientAPI struct { 208 checkTransaction *ClientCheckTransaction 209 connectWallet *ClientConnectWallet 210 getChainID *ClientGetChainID 211 listKeys *ClientListKeys 212 signTransaction *ClientSignTransaction 213 sendTransaction *ClientSendTransaction 214 } 215 216 func (a *ClientAPI) CheckTransaction(ctx context.Context, rawParams jsonrpc.Params, connectedWallet ConnectedWallet) (jsonrpc.Result, *jsonrpc.ErrorDetails) { 217 return a.checkTransaction.Handle(ctx, rawParams, connectedWallet) 218 } 219 220 func (a *ClientAPI) ConnectWallet(ctx context.Context, hostname string) (wallet.Wallet, *jsonrpc.ErrorDetails) { 221 return a.connectWallet.Handle(ctx, hostname) 222 } 223 224 func (a *ClientAPI) GetChainID(ctx context.Context) (jsonrpc.Result, *jsonrpc.ErrorDetails) { 225 return a.getChainID.Handle(ctx) 226 } 227 228 func (a *ClientAPI) ListKeys(ctx context.Context, connectedWallet ConnectedWallet) (jsonrpc.Result, *jsonrpc.ErrorDetails) { 229 return a.listKeys.Handle(ctx, connectedWallet) 230 } 231 232 func (a *ClientAPI) SignTransaction(ctx context.Context, rawParams jsonrpc.Params, connectedWallet ConnectedWallet) (jsonrpc.Result, *jsonrpc.ErrorDetails) { 233 return a.signTransaction.Handle(ctx, rawParams, connectedWallet) 234 } 235 236 func (a *ClientAPI) SendTransaction(ctx context.Context, rawParams jsonrpc.Params, connectedWallet ConnectedWallet) (jsonrpc.Result, *jsonrpc.ErrorDetails) { 237 return a.sendTransaction.Handle(ctx, rawParams, connectedWallet) 238 } 239 240 func BuildClientAPI(walletStore WalletStore, interactor Interactor, nodeSelector node.Selector, spam SpamHandler) (*ClientAPI, error) { 241 requestController := DefaultRequestController() 242 243 clientAPI := &ClientAPI{} 244 clientAPI.connectWallet = NewConnectWallet(walletStore, interactor) 245 clientAPI.getChainID = NewGetChainID(nodeSelector) 246 clientAPI.listKeys = NewListKeys(walletStore, interactor) 247 clientAPI.checkTransaction = NewClientCheckTransaction(walletStore, interactor, nodeSelector, spam, requestController) 248 clientAPI.signTransaction = NewClientSignTransaction(walletStore, interactor, nodeSelector, spam, requestController) 249 clientAPI.sendTransaction = NewClientSendTransaction(walletStore, interactor, nodeSelector, spam, requestController) 250 251 return clientAPI, nil 252 } 253 254 // AdminAPI builds the JSON-RPC API of the wallet with all the methods available. 255 // This API exposes highly-sensitive methods, and, as a result, it should be 256 // only exposed to highly-trustable applications. 257 func AdminAPI( 258 log *zap.Logger, 259 walletStore WalletStore, 260 netStore NetworkStore, 261 nodeSelectorBuilder NodeSelectorBuilder, 262 connectionsManager ConnectionsManager, 263 ) (*jsonrpc.Dispatcher, error) { 264 walletAPI := jsonrpc.NewDispatcher(log) 265 walletAPI.RegisterMethod("admin.annotate_key", NewAdminAnnotateKey(walletStore)) 266 walletAPI.RegisterMethod("admin.check_transaction", NewAdminCheckTransaction(walletStore, netStore, nodeSelectorBuilder)) 267 walletAPI.RegisterMethod("admin.close_connection", NewAdminCloseConnection(connectionsManager)) 268 walletAPI.RegisterMethod("admin.close_connections_to_hostname", NewAdminCloseConnectionsToHostname(connectionsManager)) 269 walletAPI.RegisterMethod("admin.close_connections_to_wallet", NewAdminCloseConnectionsToWallet(connectionsManager)) 270 walletAPI.RegisterMethod("admin.create_wallet", NewAdminCreateWallet(walletStore)) 271 walletAPI.RegisterMethod("admin.describe_key", NewAdminDescribeKey(walletStore)) 272 walletAPI.RegisterMethod("admin.describe_network", NewAdminDescribeNetwork(netStore)) 273 walletAPI.RegisterMethod("admin.describe_permissions", NewAdminDescribePermissions(walletStore)) 274 walletAPI.RegisterMethod("admin.describe_wallet", NewAdminDescribeWallet(walletStore)) 275 walletAPI.RegisterMethod("admin.generate_key", NewAdminGenerateKey(walletStore)) 276 walletAPI.RegisterMethod("admin.import_network", NewAdminImportNetwork(netStore)) 277 walletAPI.RegisterMethod("admin.import_wallet", NewAdminImportWallet(walletStore)) 278 walletAPI.RegisterMethod("admin.isolate_key", NewAdminIsolateKey(walletStore)) 279 walletAPI.RegisterMethod("admin.list_connections", NewAdminListConnections(connectionsManager)) 280 walletAPI.RegisterMethod("admin.list_keys", NewAdminListKeys(walletStore)) 281 walletAPI.RegisterMethod("admin.list_networks", NewAdminListNetworks(netStore)) 282 walletAPI.RegisterMethod("admin.list_permissions", NewAdminListPermissions(walletStore)) 283 walletAPI.RegisterMethod("admin.list_wallets", NewAdminListWallets(walletStore)) 284 walletAPI.RegisterMethod("admin.purge_permissions", NewAdminPurgePermissions(walletStore)) 285 walletAPI.RegisterMethod("admin.remove_network", NewAdminRemoveNetwork(netStore)) 286 walletAPI.RegisterMethod("admin.remove_wallet", NewAdminRemoveWallet(walletStore)) 287 walletAPI.RegisterMethod("admin.rename_wallet", NewAdminRenameWallet(walletStore)) 288 walletAPI.RegisterMethod("admin.revoke_permissions", NewAdminRevokePermissions(walletStore)) 289 walletAPI.RegisterMethod("admin.rotate_key", NewAdminRotateKey(walletStore)) 290 walletAPI.RegisterMethod("admin.send_raw_transaction", NewAdminSendRawTransaction(netStore, nodeSelectorBuilder)) 291 walletAPI.RegisterMethod("admin.send_transaction", NewAdminSendTransaction(walletStore, netStore, nodeSelectorBuilder)) 292 walletAPI.RegisterMethod("admin.sign_message", NewAdminSignMessage(walletStore)) 293 walletAPI.RegisterMethod("admin.sign_transaction", NewAdminSignTransaction(walletStore, netStore, nodeSelectorBuilder)) 294 walletAPI.RegisterMethod("admin.taint_key", NewAdminTaintKey(walletStore)) 295 walletAPI.RegisterMethod("admin.unlock_wallet", NewAdminUnlockWallet(walletStore)) 296 walletAPI.RegisterMethod("admin.untaint_key", NewAdminUntaintKey(walletStore)) 297 walletAPI.RegisterMethod("admin.update_network", NewAdminUpdateNetwork(netStore)) 298 walletAPI.RegisterMethod("admin.update_passphrase", NewAdminUpdatePassphrase(walletStore)) 299 walletAPI.RegisterMethod("admin.update_permissions", NewAdminUpdatePermissions(walletStore)) 300 walletAPI.RegisterMethod("admin.verify_message", NewAdminVerifyMessage()) 301 302 log.Info("the admin JSON-RPC API has been initialised") 303 304 return walletAPI, nil 305 } 306 307 func noNodeSelectionReporting(_ node.ReportType, _ string) { 308 // Nothing to do. 309 }