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  }