code.vegaprotocol.io/vega@v0.79.0/wallet/wallets/handler.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 wallets 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "sync" 23 24 "code.vegaprotocol.io/vega/commands" 25 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 26 walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1" 27 wcommands "code.vegaprotocol.io/vega/wallet/commands" 28 wcrypto "code.vegaprotocol.io/vega/wallet/crypto" 29 "code.vegaprotocol.io/vega/wallet/wallet" 30 ) 31 32 var ErrWalletDoesNotExists = errors.New("wallet does not exist") 33 34 // Store abstracts the underlying storage for wallet data. 35 type Store interface { 36 UnlockWallet(ctx context.Context, name, passphrase string) error 37 WalletExists(ctx context.Context, name string) (bool, error) 38 CreateWallet(ctx context.Context, w wallet.Wallet, passphrase string) error 39 UpdateWallet(ctx context.Context, w wallet.Wallet) error 40 GetWallet(ctx context.Context, name string) (wallet.Wallet, error) 41 GetWalletPath(name string) string 42 ListWallets(ctx context.Context) ([]string, error) 43 } 44 45 type Handler struct { 46 store Store 47 48 // just to make sure we do not access same file concurrently or the map 49 mu sync.RWMutex 50 } 51 52 func NewHandler(store Store) *Handler { 53 return &Handler{ 54 store: store, 55 } 56 } 57 58 func (h *Handler) WalletExists(name string) bool { 59 h.mu.Lock() 60 defer h.mu.Unlock() 61 62 exist, _ := h.store.WalletExists(context.Background(), name) 63 return exist 64 } 65 66 func (h *Handler) ListWallets() ([]string, error) { 67 h.mu.Lock() 68 defer h.mu.Unlock() 69 70 return h.store.ListWallets(context.Background()) 71 } 72 73 func (h *Handler) CreateWallet(name, passphrase string) (string, error) { 74 h.mu.Lock() 75 defer h.mu.Unlock() 76 77 if exists, err := h.store.WalletExists(context.Background(), name); err != nil { 78 return "", fmt.Errorf("couldn't verify the wallet existence: %w", err) 79 } else if exists { 80 return "", wallet.ErrWalletAlreadyExists 81 } 82 83 w, recoveryPhrase, err := wallet.NewHDWallet(name) 84 if err != nil { 85 return "", err 86 } 87 88 if err := h.store.CreateWallet(context.Background(), w, passphrase); err != nil { 89 return "", err 90 } 91 92 return recoveryPhrase, nil 93 } 94 95 func (h *Handler) ImportWallet(name, passphrase, recoveryPhrase string, keyDerivationVersion uint32) error { 96 h.mu.Lock() 97 defer h.mu.Unlock() 98 99 if exists, err := h.store.WalletExists(context.Background(), name); err != nil { 100 return fmt.Errorf("couldn't verify wallet existence: %w", err) 101 } else if exists { 102 return wallet.ErrWalletAlreadyExists 103 } 104 105 w, err := wallet.ImportHDWallet(name, recoveryPhrase, keyDerivationVersion) 106 if err != nil { 107 return err 108 } 109 110 if err := h.store.CreateWallet(context.Background(), w, passphrase); err != nil { 111 return err 112 } 113 114 return nil 115 } 116 117 func (h *Handler) LoginWallet(name, passphrase string) error { 118 h.mu.Lock() 119 defer h.mu.Unlock() 120 121 ctx := context.Background() 122 123 if exists, err := h.store.WalletExists(ctx, name); err != nil { 124 return fmt.Errorf("couldn't verify wallet existence: %w", err) 125 } else if !exists { 126 return ErrWalletDoesNotExists 127 } 128 129 if err := h.store.UnlockWallet(ctx, name, passphrase); err != nil { 130 if errors.Is(err, wallet.ErrWrongPassphrase) { 131 return err 132 } 133 return fmt.Errorf("couldn't unlock wallet %q: %w", name, err) 134 } 135 136 if _, err := h.store.GetWallet(ctx, name); err != nil { 137 return fmt.Errorf("couldn't get wallet %q: %w", name, err) 138 } 139 140 return nil 141 } 142 143 func (h *Handler) GenerateKeyPair(name, passphrase string, meta []wallet.Metadata) (wallet.KeyPair, error) { 144 h.mu.Lock() 145 defer h.mu.Unlock() 146 147 if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil { 148 if errors.Is(err, wallet.ErrWrongPassphrase) { 149 return nil, err 150 } 151 return nil, fmt.Errorf("couldn't unlock wallet %q: %w", name, err) 152 } 153 154 w, err := h.store.GetWallet(context.Background(), name) 155 if err != nil { 156 return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err) 157 } 158 159 kp, err := w.GenerateKeyPair(meta) 160 if err != nil { 161 return nil, err 162 } 163 164 if err := h.store.UpdateWallet(context.Background(), w); err != nil { 165 return nil, err 166 } 167 168 return kp, nil 169 } 170 171 func (h *Handler) SecureGenerateKeyPair(name, passphrase string, meta []wallet.Metadata) (string, error) { 172 kp, err := h.GenerateKeyPair(name, passphrase, meta) 173 if err != nil { 174 return "", err 175 } 176 177 return kp.PublicKey(), nil 178 } 179 180 func (h *Handler) GetPublicKey(name, pubKey string) (wallet.PublicKey, error) { 181 h.mu.RLock() 182 defer h.mu.RUnlock() 183 184 w, err := h.store.GetWallet(context.Background(), name) 185 if err != nil { 186 return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err) 187 } 188 189 return w.DescribePublicKey(pubKey) 190 } 191 192 func (h *Handler) ListPublicKeys(name string) ([]wallet.PublicKey, error) { 193 h.mu.RLock() 194 defer h.mu.RUnlock() 195 196 w, err := h.store.GetWallet(context.Background(), name) 197 if err != nil { 198 return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err) 199 } 200 201 return w.ListPublicKeys(), nil 202 } 203 204 func (h *Handler) ListKeyPairs(name string) ([]wallet.KeyPair, error) { 205 h.mu.RLock() 206 defer h.mu.RUnlock() 207 208 w, err := h.store.GetWallet(context.Background(), name) 209 if err != nil { 210 return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err) 211 } 212 213 return w.ListKeyPairs(), nil 214 } 215 216 func (h *Handler) SignAny(name string, inputData []byte, pubKey string) ([]byte, error) { 217 h.mu.RLock() 218 defer h.mu.RUnlock() 219 220 w, err := h.store.GetWallet(context.Background(), name) 221 if err != nil { 222 return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err) 223 } 224 225 return w.SignAny(pubKey, inputData) 226 } 227 228 func (h *Handler) SignTx(name string, req *walletpb.SubmitTransactionRequest, height uint64, chainID string) (*commandspb.Transaction, error) { 229 h.mu.RLock() 230 defer h.mu.RUnlock() 231 232 w, err := h.store.GetWallet(context.Background(), name) 233 if err != nil { 234 return nil, fmt.Errorf("couldn't get wallet %q: %w", name, err) 235 } 236 237 marshaledInputData, err := wcommands.ToMarshaledInputData(req, height) 238 if err != nil { 239 return nil, fmt.Errorf("couldn't marshal input data: %w", err) 240 } 241 242 pubKey := req.GetPubKey() 243 signature, err := w.SignTx(pubKey, commands.BundleInputDataForSigning(marshaledInputData, chainID)) 244 if err != nil { 245 return nil, fmt.Errorf("couldn't sign transaction: %w", err) 246 } 247 248 protoSignature := &commandspb.Signature{ 249 Value: signature.Value, 250 Algo: signature.Algo, 251 Version: signature.Version, 252 } 253 return commands.NewTransaction(pubKey, marshaledInputData, protoSignature), nil 254 } 255 256 func (h *Handler) VerifyAny(inputData, sig []byte, pubKey string) (bool, error) { 257 h.mu.RLock() 258 defer h.mu.RUnlock() 259 260 return wcrypto.VerifyMessage(&wcrypto.VerifyMessageRequest{ 261 Message: inputData, 262 Signature: sig, 263 PubKey: pubKey, 264 }) 265 } 266 267 func (h *Handler) TaintKey(name, pubKey, passphrase string) error { 268 h.mu.Lock() 269 defer h.mu.Unlock() 270 271 if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil { 272 if errors.Is(err, wallet.ErrWrongPassphrase) { 273 return err 274 } 275 return fmt.Errorf("couldn't unlock wallet %q: %w", name, err) 276 } 277 278 w, err := h.store.GetWallet(context.Background(), name) 279 if err != nil { 280 return fmt.Errorf("couldn't get wallet %q: %w", name, err) 281 } 282 283 err = w.TaintKey(pubKey) 284 if err != nil { 285 return err 286 } 287 288 if err := h.store.UpdateWallet(context.Background(), w); err != nil { 289 return err 290 } 291 292 return nil 293 } 294 295 func (h *Handler) UntaintKey(name string, pubKey string, passphrase string) error { 296 h.mu.Lock() 297 defer h.mu.Unlock() 298 299 if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil { 300 if errors.Is(err, wallet.ErrWrongPassphrase) { 301 return err 302 } 303 return fmt.Errorf("couldn't unlock wallet %q: %w", name, err) 304 } 305 306 w, err := h.store.GetWallet(context.Background(), name) 307 if err != nil { 308 return fmt.Errorf("couldn't get wallet %q: %w", name, err) 309 } 310 311 err = w.UntaintKey(pubKey) 312 if err != nil { 313 return err 314 } 315 316 if err := h.store.UpdateWallet(context.Background(), w); err != nil { 317 return err 318 } 319 320 return nil 321 } 322 323 func (h *Handler) UpdateMeta(name, pubKey, passphrase string, meta []wallet.Metadata) error { 324 h.mu.Lock() 325 defer h.mu.Unlock() 326 327 if err := h.store.UnlockWallet(context.Background(), name, passphrase); err != nil { 328 if errors.Is(err, wallet.ErrWrongPassphrase) { 329 return err 330 } 331 return fmt.Errorf("couldn't unlock wallet %q: %w", name, err) 332 } 333 334 w, err := h.store.GetWallet(context.Background(), name) 335 if err != nil { 336 return fmt.Errorf("couldn't get wallet %q: %w", name, err) 337 } 338 339 _, err = w.AnnotateKey(pubKey, meta) 340 if err != nil { 341 return err 342 } 343 344 if err := h.store.UpdateWallet(context.Background(), w); err != nil { 345 return err 346 } 347 348 return nil 349 } 350 351 func (h *Handler) GetWalletPath(name string) (string, error) { 352 return h.store.GetWalletPath(name), nil 353 }