code.vegaprotocol.io/vega@v0.79.0/wallet/api/interactor/interaction.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 interactor 17 18 import ( 19 "encoding/json" 20 "time" 21 22 "github.com/mitchellh/mapstructure" 23 ) 24 25 const ( 26 CancelRequestName InteractionName = "CANCEL_REQUEST" 27 DecisionName InteractionName = "DECISION" 28 EnteredPassphraseName InteractionName = "ENTERED_PASSPHRASE" 29 ErrorOccurredName InteractionName = "ERROR_OCCURRED" 30 InteractionSessionBeganName InteractionName = "INTERACTION_SESSION_BEGAN" 31 InteractionSessionEndedName InteractionName = "INTERACTION_SESSION_ENDED" 32 LogName InteractionName = "LOG" 33 RequestPassphraseName InteractionName = "REQUEST_PASSPHRASE" 34 RequestPermissionsReviewName InteractionName = "REQUEST_PERMISSIONS_REVIEW" 35 RequestSucceededName InteractionName = "REQUEST_SUCCEEDED" 36 RequestTransactionReviewForSendingName InteractionName = "REQUEST_TRANSACTION_REVIEW_FOR_SENDING" 37 RequestTransactionReviewForSigningName InteractionName = "REQUEST_TRANSACTION_REVIEW_FOR_SIGNING" 38 RequestTransactionReviewForCheckingName InteractionName = "REQUEST_TRANSACTION_REVIEW_FOR_CHECKING" 39 RequestWalletConnectionReviewName InteractionName = "REQUEST_WALLET_CONNECTION_REVIEW" 40 RequestWalletSelectionName InteractionName = "REQUEST_WALLET_SELECTION" 41 SelectedWalletName InteractionName = "SELECTED_WALLET" 42 TransactionFailedName InteractionName = "TRANSACTION_FAILED" 43 TransactionSucceededName InteractionName = "TRANSACTION_SUCCEEDED" 44 WalletConnectionDecisionName InteractionName = "WALLET_CONNECTION_DECISION" 45 ) 46 47 type InteractionName string 48 49 // Interaction wraps the messages the JSON-RPC API sends to the wallet front-end 50 // along with information about the context. 51 type Interaction struct { 52 // TraceID is an identifier specifically made for client front-end to keep 53 // track of a transaction during all of its lifetime, from transaction 54 // review to sending confirmation and in-memory history. 55 // It shouldn't be confused with the transaction hash that is the 56 // transaction identifier. 57 TraceID string `json:"traceID"` 58 59 // Name is the name of the interaction. This helps to figure out how the 60 // data payload should be parsed. 61 Name InteractionName `json:"name"` 62 63 // Data is the generic field that hold the data of the specific interaction. 64 Data interface{} `json:"data"` 65 } 66 67 func (f *Interaction) UnmarshalJSON(data []byte) error { 68 input := &struct { 69 TraceID string `json:"traceID"` 70 Name InteractionName `json:"name"` 71 Data map[string]interface{} `json:"data"` 72 }{} 73 74 if err := json.Unmarshal(data, &input); err != nil { 75 return err 76 } 77 78 f.TraceID = input.TraceID 79 f.Name = input.Name 80 81 switch input.Name { 82 case CancelRequestName: 83 data := CancelRequest{} 84 if err := mapstructure.Decode(input.Data, &data); err != nil { 85 return err 86 } 87 f.Data = data 88 case RequestWalletConnectionReviewName: 89 data := RequestWalletConnectionReview{} 90 if err := mapstructure.Decode(input.Data, &data); err != nil { 91 return err 92 } 93 f.Data = data 94 case RequestWalletSelectionName: 95 data := RequestWalletSelection{} 96 if err := mapstructure.Decode(input.Data, &data); err != nil { 97 return err 98 } 99 f.Data = data 100 case RequestPassphraseName: 101 data := RequestPassphrase{} 102 if err := mapstructure.Decode(input.Data, &data); err != nil { 103 return err 104 } 105 f.Data = data 106 case RequestPermissionsReviewName: 107 data := RequestPermissionsReview{} 108 if err := mapstructure.Decode(input.Data, &data); err != nil { 109 return err 110 } 111 f.Data = data 112 case RequestTransactionReviewForSendingName: 113 data := RequestTransactionReviewForSending{} 114 if err := mapstructure.Decode(input.Data, &data); err != nil { 115 return err 116 } 117 f.Data = data 118 case RequestTransactionReviewForSigningName: 119 data := RequestTransactionReviewForSigning{} 120 if err := mapstructure.Decode(input.Data, &data); err != nil { 121 return err 122 } 123 f.Data = data 124 case RequestTransactionReviewForCheckingName: 125 data := RequestTransactionReviewForChecking{} 126 if err := mapstructure.Decode(input.Data, &data); err != nil { 127 return err 128 } 129 f.Data = data 130 case WalletConnectionDecisionName: 131 data := WalletConnectionDecision{} 132 if err := mapstructure.Decode(input.Data, &data); err != nil { 133 return err 134 } 135 f.Data = data 136 case DecisionName: 137 data := Decision{} 138 if err := mapstructure.Decode(input.Data, &data); err != nil { 139 return err 140 } 141 f.Data = data 142 case EnteredPassphraseName: 143 data := EnteredPassphrase{} 144 if err := mapstructure.Decode(input.Data, &data); err != nil { 145 return err 146 } 147 f.Data = data 148 case SelectedWalletName: 149 data := SelectedWallet{} 150 if err := mapstructure.Decode(input.Data, &data); err != nil { 151 return err 152 } 153 f.Data = data 154 case InteractionSessionBeganName: 155 data := InteractionSessionBegan{} 156 if err := mapstructure.Decode(input.Data, &data); err != nil { 157 return err 158 } 159 f.Data = data 160 case InteractionSessionEndedName: 161 data := InteractionSessionEnded{} 162 if err := mapstructure.Decode(input.Data, &data); err != nil { 163 return err 164 } 165 f.Data = data 166 case RequestSucceededName: 167 data := RequestSucceeded{} 168 if err := mapstructure.Decode(input.Data, &data); err != nil { 169 return err 170 } 171 f.Data = data 172 case ErrorOccurredName: 173 data := ErrorOccurred{} 174 if err := mapstructure.Decode(input.Data, &data); err != nil { 175 return err 176 } 177 f.Data = data 178 case TransactionSucceededName: 179 data := TransactionSucceeded{} 180 if err := mapstructure.Decode(input.Data, &data); err != nil { 181 return err 182 } 183 f.Data = data 184 case TransactionFailedName: 185 data := TransactionFailed{} 186 if err := mapstructure.Decode(input.Data, &data); err != nil { 187 return err 188 } 189 f.Data = data 190 case LogName: 191 data := Log{} 192 if err := mapstructure.Decode(input.Data, &data); err != nil { 193 return err 194 } 195 f.Data = data 196 } 197 198 return nil 199 } 200 201 // RequestWalletConnectionReview is a request emitted when a third-party 202 // application wants to connect to a wallet. 203 type RequestWalletConnectionReview struct { 204 Hostname string `json:"hostname"` 205 206 StepNumber uint8 `json:"stepNumber"` 207 208 // ResponseCh is the channel in which the response to that request is expected. 209 ResponseCh chan<- Interaction `json:"-"` 210 211 // ControlCh is the channel we use to communicate state of the request to 212 // the UI. If the request gets canceled on the third-party application side, 213 // we close this channel and the UI, that is listening to it, has to react 214 // accordingly. 215 ControlCh <-chan error `json:"-"` 216 } 217 218 // RequestWalletSelection is a request emitted when the service requires the user 219 // to select a wallet. It is emitted after the user approved the wallet connection 220 // from a third-party application. 221 // It should be answered by an interactor.SelectedWallet response. 222 type RequestWalletSelection struct { 223 Hostname string `json:"hostname"` 224 AvailableWallets []string `json:"availableWallets"` 225 226 StepNumber uint8 `json:"stepNumber"` 227 228 // ResponseCh is the channel in which the response to that request is expected. 229 ResponseCh chan<- Interaction `json:"-"` 230 231 // ControlCh is the channel we use to communicate state of the request to 232 // the UI. If the request gets canceled on the third-party application side, 233 // we close this channel and the UI, that is listening to it, has to react 234 // accordingly. 235 ControlCh chan error `json:"-"` 236 } 237 238 // RequestPassphrase is a request emitted when the service wants to confirm 239 // the user has access to the wallet. 240 // It should be answered by an interactor.EnteredPassphrase response. 241 type RequestPassphrase struct { 242 Wallet string `json:"wallet"` 243 Reason string `json:"reason"` 244 245 StepNumber uint8 `json:"stepNumber"` 246 247 // ResponseCh is the channel in which the response to that request is expected. 248 ResponseCh chan<- Interaction `json:"-"` 249 250 // ControlCh is the channel we use to communicate state of the request to 251 // the UI. If the request gets canceled on the third-party application side, 252 // we close this channel and the UI, that is listening to it, has to react 253 // accordingly. 254 ControlCh chan error `json:"-"` 255 } 256 257 // RequestPermissionsReview is a review request emitted when a third-party 258 // application performs an operation that requires an update to the permissions. 259 type RequestPermissionsReview struct { 260 Hostname string `json:"hostname"` 261 Wallet string `json:"wallet"` 262 Permissions map[string]string `json:"permissions"` 263 264 StepNumber uint8 `json:"stepNumber"` 265 266 // ResponseCh is the channel in which the response to that request is expected. 267 ResponseCh chan<- Interaction `json:"-"` 268 269 // ControlCh is the channel we use to communicate state of the request to 270 // the UI. If the request gets canceled on the third-party application side, 271 // we close this channel and the UI, that is listening to it, has to react 272 // accordingly. 273 ControlCh chan error `json:"-"` 274 } 275 276 // RequestTransactionReviewForSending is a review request emitted when a third-party 277 // application wants to send a transaction. 278 type RequestTransactionReviewForSending struct { 279 Hostname string `json:"hostname"` 280 Wallet string `json:"wallet"` 281 PublicKey string `json:"publicKey"` 282 Transaction string `json:"transaction"` 283 ReceivedAt time.Time `json:"receivedAt"` 284 285 StepNumber uint8 `json:"stepNumber"` 286 287 // ResponseCh is the channel in which the response to that request is expected. 288 ResponseCh chan<- Interaction `json:"-"` 289 290 // ControlCh is the channel we use to communicate state of the request to 291 // the UI. If the request gets canceled on the third-party application side, 292 // we close this channel and the UI, that is listening to it, has to react 293 // accordingly. 294 ControlCh chan error `json:"-"` 295 } 296 297 // RequestTransactionReviewForChecking is a review request when a third-party 298 // application wants the user to check a transaction. 299 type RequestTransactionReviewForChecking struct { 300 Hostname string `json:"hostname"` 301 Wallet string `json:"wallet"` 302 PublicKey string `json:"publicKey"` 303 Transaction string `json:"transaction"` 304 ReceivedAt time.Time `json:"receivedAt"` 305 306 StepNumber uint8 `json:"stepNumber"` 307 308 // ResponseCh is the channel in which the response to that request is expected. 309 ResponseCh chan<- Interaction `json:"-"` 310 311 // ControlCh is the channel we use to communicate state of the request to 312 // the UI. If the request gets canceled on the third-party application side, 313 // we close this channel and the UI, that is listening to it, has to react 314 // accordingly. 315 ControlCh chan error `json:"-"` 316 } 317 318 // RequestTransactionReviewForSigning is a review request when a third-party 319 // application wants to sign a transaction. 320 type RequestTransactionReviewForSigning struct { 321 Hostname string `json:"hostname"` 322 Wallet string `json:"wallet"` 323 PublicKey string `json:"publicKey"` 324 Transaction string `json:"transaction"` 325 ReceivedAt time.Time `json:"receivedAt"` 326 327 StepNumber uint8 `json:"stepNumber"` 328 329 // ResponseCh is the channel in which the response to that request is expected. 330 ResponseCh chan<- Interaction `json:"-"` 331 332 // ControlCh is the channel we use to communicate state of the request to 333 // the UI. If the request gets canceled on the third-party application side, 334 // we close this channel and the UI, that is listening to it, has to react 335 // accordingly. 336 ControlCh chan error `json:"-"` 337 } 338 339 // WalletConnectionDecision is a specific response for interactor.RequestWalletConnectionReview. 340 type WalletConnectionDecision struct { 341 // ConnectionApproval tells if the third-party application is authorized 342 // to connect to a wallet. 343 // The value is the string representation of a preferences.ConnectionApproval. 344 ConnectionApproval string `json:"connectionApproval"` 345 } 346 347 // Decision is a generic response for the following review requests: 348 // - RequestPermissionsReview 349 // - RequestTransactionReviewForSigning 350 // - RequestTransactionReviewForSending 351 type Decision struct { 352 // Approved is set to true if the request is accepted by the user, false 353 // otherwise. 354 Approved bool `json:"approved"` 355 } 356 357 // EnteredPassphrase contains the passphrase of a given wallet the user 358 // entered. It's a response to the interactor.RequestPassphrase. 359 type EnteredPassphrase struct { 360 Passphrase string `json:"passphrase"` 361 } 362 363 // SelectedWallet contains the wallet chosen by the user for a given action. 364 type SelectedWallet struct { 365 Wallet string `json:"wallet"` 366 } 367 368 // CancelRequest cancels a request that is waiting for user inputs. 369 // It can't cancel a request when there is no response awaited. 370 type CancelRequest struct{} 371 372 // InteractionSessionBegan is a notification that is emitted when the interaction 373 // session begin. It only carries informational value on a request lifecycle. This 374 // is the first notification to be emitted and is always emitted when a request 375 // comes in. 376 type InteractionSessionBegan struct { 377 Workflow string `json:"workflow"` 378 MaximumNumberOfSteps uint8 `json:"maximumNumberOfSteps"` 379 } 380 381 // InteractionSessionEnded is a notification that is emitted when the interaction 382 // session ended. This is the last notification to be emitted and is always emitted, 383 // regardless of the request status. It only carries informational value on a 384 // request lifecycle. The success or failure status of a request is carried by 385 // the interactor.RequestSucceeded and interactor.ErrorOccurred notifications, 386 // respectively. 387 // Nothing should be expected after receiving this notification. 388 type InteractionSessionEnded struct{} 389 390 // ErrorOccurred is a generic notification emitted when the something failed. 391 // This notification can wrap an internal failure as much as a user input error. 392 // Receiving this notification doesn't necessarily mean the overall 393 // request failed. The request should be considered as failed when this notification 394 // is followed by the interactor.InteractionSessionEnded notification. 395 type ErrorOccurred struct { 396 // Type is an enumeration that gives information about the origin of the error. 397 // The value is the string representation of an api.ErrorType. 398 Type string `json:"type"` 399 400 // Error is the error message describing the reason of the failure. 401 Error string `json:"error"` 402 } 403 404 // RequestSucceeded is a generic notification emitted when the request succeeded, 405 // meaning no error has been encountered. 406 // This notification is emitted only once. 407 type RequestSucceeded struct { 408 // Message can contain a custom success message. 409 Message string `json:"message"` 410 411 StepNumber uint8 `json:"stepNumber"` 412 } 413 414 // TransactionSucceeded is a notification sent when the sending of a 415 // transaction succeeded. It replaces the RequestSucceeded notification as it 416 // carries specific information that wallet front-ends may use for history. 417 // This notification is emitted only once. 418 type TransactionSucceeded struct { 419 // TxHash is the hash of the transaction that is used to uniquely identify 420 // a transaction. It can be used to retrieve a transaction in the explorer. 421 TxHash string `json:"txHash"` 422 423 // DeserializedInputData is the input data bundled in the transaction in a 424 // human-readable format. 425 DeserializedInputData string `json:"deserializedInputData"` 426 427 // Tx is the true representation of the transaction that is sent to the 428 // network. 429 Tx string `json:"tx"` 430 431 // SentAt is the time a which the transaction has been sent to the network. 432 // It's useful to build a list of the sending in a chronological order on 433 // the front-ends. 434 SentAt time.Time `json:"sentAt"` 435 436 // Node contains all the information related to the node selected for the 437 // sending of the transaction. 438 Node SelectedNode `json:"node"` 439 440 StepNumber uint8 `json:"stepNumber"` 441 } 442 443 // TransactionFailed is a notification sent when the sending of a 444 // transaction failed for any reason. It replaces the ErrorOccurred notification 445 // as it carries specific information that wallet front-ends may use for 446 // investigation. 447 // This notification is emitted only once. 448 type TransactionFailed struct { 449 // DeserializedInputData is the input data bundled in the transaction in a 450 // human-readable format. 451 DeserializedInputData string `json:"deserializedInputData"` 452 453 // Tx is the true representation of the transaction that is sent to the 454 // network. 455 Tx string `json:"tx"` 456 457 // Error is the error message describing the reason of the failure. 458 Error error `json:"error"` 459 460 // SentAt is the time a which the transaction has been sent to the network. 461 // It's useful to build a list of the sending in a chronological order on 462 // the front-ends. 463 SentAt time.Time `json:"sentAt"` 464 465 // Node contains all the information related to the node selected for the 466 // sending of the transaction. 467 Node SelectedNode `json:"node"` 468 469 StepNumber uint8 `json:"stepNumber"` 470 } 471 472 type SelectedNode struct { 473 Host string `json:"host"` 474 } 475 476 // Log is a generic event that shouldn't be confused with a notification. A log 477 // is conceptually different. Whatever the type (error, warning...), a log is just 478 // an information about the internal processing that we think is worth to 479 // broadcast to wallet front-ends. That said, it can be safely ignored if not 480 // needed. That is where is differs from the notifications. 481 type Log struct { 482 // Type is an enumeration that gives information about the level of log. 483 // The value is the string representation of an api.LogType. 484 Type string `json:"type"` 485 486 // Message is the log message itself. 487 Message string `json:"message"` 488 }