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, &params); 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  }