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