github.com/klaytn/klaytn@v1.12.1/cmd/utils/nodecmd/util.go (about)

     1  // Modifications Copyright 2023 The klaytn Authors
     2  // Copyright 2016 The go-ethereum Authors
     3  // This file is part of go-ethereum.
     4  //
     5  // go-ethereum is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // go-ethereum is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package nodecmd
    19  
    20  import (
    21  	"encoding/binary"
    22  	"encoding/hex"
    23  	"encoding/json"
    24  	"errors"
    25  	"fmt"
    26  	"os"
    27  
    28  	"github.com/klaytn/klaytn/accounts/keystore"
    29  	"github.com/klaytn/klaytn/blockchain/types"
    30  	"github.com/klaytn/klaytn/common"
    31  	"github.com/klaytn/klaytn/common/hexutil"
    32  	"github.com/klaytn/klaytn/consensus/istanbul"
    33  	"github.com/klaytn/klaytn/consensus/istanbul/backend"
    34  	"github.com/klaytn/klaytn/crypto"
    35  	"github.com/klaytn/klaytn/crypto/sha3"
    36  	"github.com/klaytn/klaytn/governance"
    37  	"github.com/klaytn/klaytn/params"
    38  	"github.com/klaytn/klaytn/rlp"
    39  	"github.com/urfave/cli/v2"
    40  )
    41  
    42  const (
    43  	DECODE_EXTRA = "decode-extra"
    44  	DECODE_VOTE  = "decode-vote"
    45  	DECODE_GOV   = "decode-gov"
    46  	DECRYPT_KEY  = "decrypt-keystore"
    47  )
    48  
    49  var ErrInvalidCmd = errors.New("Invalid command. Check usage through --help command")
    50  
    51  var UtilCommand = &cli.Command{
    52  	Name:     "util",
    53  	Usage:    "offline utility",
    54  	Category: "MISCELLANEOUS COMMANDS",
    55  	Subcommands: []*cli.Command{
    56  		{
    57  			Name:        DECODE_EXTRA,
    58  			Usage:       "<header file (json format)>",
    59  			Action:      action,
    60  			Description: "Decode header extra field",
    61  		},
    62  		{
    63  			Name:        DECODE_VOTE,
    64  			Usage:       "<hex bytes>",
    65  			Action:      action,
    66  			Description: "Decode header vote field",
    67  		},
    68  		{
    69  			Name:        DECODE_GOV,
    70  			Usage:       "<hex bytes>",
    71  			Action:      action,
    72  			Description: "Decode header governance field",
    73  		},
    74  		{
    75  			Name:        DECRYPT_KEY,
    76  			Usage:       "<keystore path> <password>",
    77  			Action:      action,
    78  			Description: "Decrypt keystore",
    79  		},
    80  	},
    81  }
    82  
    83  func action(ctx *cli.Context) error {
    84  	var (
    85  		m   map[string]interface{}
    86  		err error
    87  	)
    88  	switch ctx.Command.Name {
    89  	case DECODE_EXTRA:
    90  		if ctx.Args().Len() != 1 {
    91  			return ErrInvalidCmd
    92  		}
    93  		m, err = decodeExtra(ctx.Args().Get(0))
    94  	case DECODE_VOTE:
    95  		if ctx.Args().Len() != 1 {
    96  			return ErrInvalidCmd
    97  		}
    98  		m, err = decodeVote(hex2Bytes(ctx.Args().Get(0)))
    99  	case DECODE_GOV:
   100  		if ctx.Args().Len() != 1 {
   101  			return ErrInvalidCmd
   102  		}
   103  		m, err = decodeGov(hex2Bytes(ctx.Args().Get(0)))
   104  	case DECRYPT_KEY:
   105  		if ctx.Args().Len() != 2 {
   106  			return ErrInvalidCmd
   107  		}
   108  		keystorePath, passwd := ctx.Args().Get(0), ctx.Args().Get(1)
   109  		m, err = extractKeypair(keystorePath, passwd)
   110  	default:
   111  		return ErrInvalidCmd
   112  	}
   113  	if err == nil {
   114  		prettyPrint(m)
   115  	}
   116  	return err
   117  }
   118  
   119  func hex2Bytes(s string) []byte {
   120  	if data, err := hexutil.Decode(s); err == nil {
   121  		return data
   122  	} else {
   123  		panic(err)
   124  	}
   125  }
   126  
   127  func prettyPrint(m map[string]interface{}) {
   128  	if b, err := json.MarshalIndent(m, "", "  "); err == nil {
   129  		fmt.Println(string(b))
   130  	} else {
   131  		panic(err)
   132  	}
   133  }
   134  
   135  func extractKeypair(keystorePath, passwd string) (map[string]interface{}, error) {
   136  	keyjson, err := os.ReadFile(keystorePath)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	key, err := keystore.DecryptKey(keyjson, passwd)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	addr := key.GetAddress().String()
   145  	pubkey := key.GetPrivateKey().PublicKey
   146  	privkey := key.GetPrivateKey()
   147  	m := make(map[string]interface{})
   148  	m["addr"] = addr
   149  	m["privkey"] = hex.EncodeToString(crypto.FromECDSA(privkey))
   150  	m["pubkey"] = hex.EncodeToString(crypto.FromECDSAPub(&pubkey))
   151  	return m, nil
   152  }
   153  
   154  func decodeGov(bytes []byte) (map[string]interface{}, error) {
   155  	var b []byte
   156  	m := make(map[string]interface{})
   157  	if err := rlp.DecodeBytes(bytes, &b); err == nil {
   158  		if err := json.Unmarshal(b, &m); err == nil {
   159  			return m, nil
   160  		} else {
   161  			return nil, err
   162  		}
   163  	} else {
   164  		return nil, err
   165  	}
   166  }
   167  
   168  func parseHeaderFile(headerFile string) (*types.Header, common.Hash, error) {
   169  	header := new(types.Header)
   170  	bytes, err := os.ReadFile(headerFile)
   171  	if err != nil {
   172  		return nil, common.Hash{}, err
   173  	}
   174  	if err = json.Unmarshal(bytes, &header); err != nil {
   175  		return nil, common.Hash{}, err
   176  	}
   177  	var hash common.Hash
   178  	hasher := sha3.NewKeccak256()
   179  	rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false))
   180  	hasher.Sum(hash[:0])
   181  	return header, hash, nil
   182  }
   183  
   184  func decodeExtra(headerFile string) (map[string]interface{}, error) {
   185  	header, sigHash, err := parseHeaderFile(headerFile)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	istanbulExtra, err := types.ExtractIstanbulExtra(header)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	validators := make([]string, len(istanbulExtra.Validators))
   194  	for idx, addr := range istanbulExtra.Validators {
   195  		validators[idx] = addr.String()
   196  	}
   197  	proposer, err := istanbul.GetSignatureAddress(sigHash.Bytes(), istanbulExtra.Seal)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	committers, err := backend.RecoverCommittedSeals(istanbulExtra, header.Hash())
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	cSeals := make([]string, len(istanbulExtra.CommittedSeal))
   206  	for i := 0; i < len(cSeals); i++ {
   207  		cSeals[i] = hexutil.Encode(istanbulExtra.CommittedSeal[i])
   208  	}
   209  
   210  	m := make(map[string]interface{})
   211  	m["hash"] = header.Hash().Hex()
   212  	m["sigHash"] = sigHash.Hex()
   213  	m["validators"] = validators
   214  	m["seal"] = hexutil.Encode(istanbulExtra.Seal)
   215  	m["committedSeal"] = cSeals
   216  	m["committers"] = committers
   217  	m["validatorSize"] = len(validators)
   218  	m["committedSealSize"] = len(cSeals)
   219  	m["proposer"] = proposer.String()
   220  	m["round"] = header.Round()
   221  	return m, nil
   222  }
   223  
   224  func decodeVote(bytes []byte) (map[string]interface{}, error) {
   225  	vote := new(governance.GovernanceVote)
   226  	m := make(map[string]interface{})
   227  	if err := rlp.DecodeBytes(bytes, &vote); err == nil {
   228  		m["validator"] = vote.Validator.String()
   229  		m["key"] = vote.Key
   230  		switch governance.GovernanceKeyMap[vote.Key] {
   231  		case params.GovernanceMode, params.MintingAmount, params.MinimumStake, params.Ratio, params.Kip82Ratio:
   232  			m["value"] = string(vote.Value.([]uint8))
   233  		case params.GoverningNode, params.GovParamContract:
   234  			m["value"] = common.BytesToAddress(vote.Value.([]uint8)).String()
   235  		case params.Epoch, params.CommitteeSize, params.UnitPrice, params.DeriveShaImpl, params.StakeUpdateInterval,
   236  			params.ProposerRefreshInterval, params.ConstTxGasHumanReadable, params.Policy, params.Timeout,
   237  			params.LowerBoundBaseFee, params.UpperBoundBaseFee, params.GasTarget, params.MaxBlockGasUsedForBaseFee, params.BaseFeeDenominator:
   238  			v := vote.Value.([]uint8)
   239  			v = append(make([]byte, 8-len(v)), v...)
   240  			m["value"] = binary.BigEndian.Uint64(v)
   241  		case params.UseGiniCoeff, params.DeferredTxFee:
   242  			v := vote.Value.([]uint8)
   243  			v = append(make([]byte, 8-len(v)), v...)
   244  			if binary.BigEndian.Uint64(v) != uint64(0) {
   245  				m["value"] = true
   246  			} else {
   247  				m["value"] = false
   248  			}
   249  		case params.AddValidator, params.RemoveValidator:
   250  			if v, ok := vote.Value.([]uint8); ok {
   251  				m["value"] = common.BytesToAddress(v)
   252  			} else if addresses, ok := vote.Value.([]interface{}); ok {
   253  				if len(addresses) == 0 {
   254  					return nil, governance.ErrValueTypeMismatch
   255  				}
   256  				var nodeAddresses []common.Address
   257  				for _, item := range addresses {
   258  					if in, ok := item.([]uint8); !ok || len(in) != common.AddressLength {
   259  						return nil, governance.ErrValueTypeMismatch
   260  					}
   261  					nodeAddresses = append(nodeAddresses, common.BytesToAddress(item.([]uint8)))
   262  				}
   263  				m["value"] = nodeAddresses
   264  			} else {
   265  				return nil, governance.ErrValueTypeMismatch
   266  			}
   267  		}
   268  		return m, nil
   269  	} else {
   270  		return nil, err
   271  	}
   272  }