code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_rotate_key.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 "encoding/base64" 21 "fmt" 22 23 "code.vegaprotocol.io/vega/commands" 24 "code.vegaprotocol.io/vega/libs/jsonrpc" 25 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 26 27 "github.com/golang/protobuf/proto" 28 "github.com/mitchellh/mapstructure" 29 ) 30 31 type AdminRotateKeyParams struct { 32 Wallet string `json:"wallet"` 33 FromPublicKey string `json:"fromPublicKey"` 34 ToPublicKey string `json:"toPublicKey"` 35 ChainID string `json:"chainID"` 36 SubmissionBlockHeight uint64 `json:"submissionBlockHeight"` 37 EnactmentBlockHeight uint64 `json:"enactmentBlockHeight"` 38 } 39 40 type AdminRotateKeyResult struct { 41 MasterPublicKey string `json:"masterPublicKey"` 42 EncodedTransaction string `json:"encodedTransaction"` 43 } 44 45 type AdminRotateKey struct { 46 walletStore WalletStore 47 } 48 49 // Handle create a transaction to rotate the keys. 50 func (h *AdminRotateKey) Handle(ctx context.Context, rawParams jsonrpc.Params) (jsonrpc.Result, *jsonrpc.ErrorDetails) { 51 params, err := validateAdminRotateKeyParams(rawParams) 52 if err != nil { 53 return nil, InvalidParams(err) 54 } 55 56 if exist, err := h.walletStore.WalletExists(ctx, params.Wallet); err != nil { 57 return nil, InternalError(fmt.Errorf("could not verify the wallet exists: %w", err)) 58 } else if !exist { 59 return nil, InvalidParams(ErrWalletDoesNotExist) 60 } 61 62 alreadyUnlocked, err := h.walletStore.IsWalletAlreadyUnlocked(ctx, params.Wallet) 63 if err != nil { 64 return nil, InternalError(fmt.Errorf("could not verify whether the wallet is already unlock or not: %w", err)) 65 } 66 if !alreadyUnlocked { 67 return nil, RequestNotPermittedError(ErrWalletIsLocked) 68 } 69 70 w, err := h.walletStore.GetWallet(ctx, params.Wallet) 71 if err != nil { 72 return nil, InternalError(fmt.Errorf("could not retrieve the wallet: %w", err)) 73 } 74 75 if w.IsIsolated() { 76 return nil, InvalidParams(ErrCannotRotateKeysOnIsolatedWallet) 77 } 78 79 if !w.HasPublicKey(params.FromPublicKey) { 80 return nil, InvalidParams(ErrCurrentPublicKeyDoesNotExist) 81 } 82 83 if !w.HasPublicKey(params.ToPublicKey) { 84 return nil, InvalidParams(ErrNextPublicKeyDoesNotExist) 85 } 86 87 currentPublicKey, err := w.DescribePublicKey(params.FromPublicKey) 88 if err != nil { 89 return nil, InternalError(fmt.Errorf("could not retrieve the current public key: %w", err)) 90 } 91 92 nextPublicKey, err := w.DescribePublicKey(params.ToPublicKey) 93 if err != nil { 94 return nil, InternalError(fmt.Errorf("could not retrieve the next public key: %w", err)) 95 } 96 97 if nextPublicKey.IsTainted() { 98 return nil, InvalidParams(ErrNextPublicKeyIsTainted) 99 } 100 101 currentPubKeyHash, err := currentPublicKey.Hash() 102 if err != nil { 103 return nil, InternalError(fmt.Errorf("could not hash the current public key: %w", err)) 104 } 105 106 inputData := commands.NewInputData(params.SubmissionBlockHeight) 107 inputData.Command = &commandspb.InputData_KeyRotateSubmission{ 108 KeyRotateSubmission: &commandspb.KeyRotateSubmission{ 109 NewPubKeyIndex: nextPublicKey.Index(), 110 NewPubKey: nextPublicKey.Key(), 111 TargetBlock: params.EnactmentBlockHeight, 112 CurrentPubKeyHash: currentPubKeyHash, 113 }, 114 } 115 116 marshaledInputData, err := commands.MarshalInputData(inputData) 117 if err != nil { 118 return nil, InternalError(fmt.Errorf("could not build the key rotation transaction: %w", err)) 119 } 120 121 masterKey, err := w.MasterKey() 122 if err != nil { 123 return nil, InternalError(fmt.Errorf("could not retrieve master key to sign the key rotation transaction: %w", err)) 124 } 125 126 rotationSignature, err := masterKey.Sign(commands.BundleInputDataForSigning(marshaledInputData, params.ChainID)) 127 if err != nil { 128 return nil, InternalError(fmt.Errorf("could not sign the key rotation transaction: %w", err)) 129 } 130 131 protoSignature := &commandspb.Signature{ 132 Value: rotationSignature.Value, 133 Algo: rotationSignature.Algo, 134 Version: rotationSignature.Version, 135 } 136 137 transaction := commands.NewTransaction(masterKey.PublicKey(), marshaledInputData, protoSignature) 138 rawTransaction, err := proto.Marshal(transaction) 139 if err != nil { 140 return nil, InternalError(fmt.Errorf("could not bundle the key rotation transaction: %w", err)) 141 } 142 143 return AdminRotateKeyResult{ 144 MasterPublicKey: masterKey.PublicKey(), 145 EncodedTransaction: base64.StdEncoding.EncodeToString(rawTransaction), 146 }, nil 147 } 148 149 func validateAdminRotateKeyParams(rawParams jsonrpc.Params) (AdminRotateKeyParams, error) { 150 if rawParams == nil { 151 return AdminRotateKeyParams{}, ErrParamsRequired 152 } 153 154 params := AdminRotateKeyParams{} 155 if err := mapstructure.Decode(rawParams, ¶ms); err != nil { 156 return AdminRotateKeyParams{}, ErrParamsDoNotMatch 157 } 158 159 if params.Wallet == "" { 160 return AdminRotateKeyParams{}, ErrWalletIsRequired 161 } 162 163 if params.ChainID == "" { 164 return AdminRotateKeyParams{}, ErrChainIDIsRequired 165 } 166 167 if params.FromPublicKey == "" { 168 return AdminRotateKeyParams{}, ErrCurrentPublicKeyIsRequired 169 } 170 171 if params.ToPublicKey == "" { 172 return AdminRotateKeyParams{}, ErrNextPublicKeyIsRequired 173 } 174 175 if params.ToPublicKey == params.FromPublicKey { 176 return AdminRotateKeyParams{}, ErrNextAndCurrentPublicKeysCannotBeTheSame 177 } 178 179 if params.SubmissionBlockHeight == 0 { 180 return AdminRotateKeyParams{}, ErrSubmissionBlockHeightIsRequired 181 } 182 183 if params.EnactmentBlockHeight == 0 { 184 return AdminRotateKeyParams{}, ErrEnactmentBlockHeightIsRequired 185 } 186 187 if params.EnactmentBlockHeight <= params.SubmissionBlockHeight { 188 return AdminRotateKeyParams{}, ErrEnactmentBlockHeightMustBeGreaterThanSubmissionOne 189 } 190 191 return params, nil 192 } 193 194 func NewAdminRotateKey( 195 walletStore WalletStore, 196 ) *AdminRotateKey { 197 return &AdminRotateKey{ 198 walletStore: walletStore, 199 } 200 }