github.com/klaytn/klaytn@v1.12.1/api/api_private_account.go (about)

     1  // Modifications Copyright 2019 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser 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  // The go-ethereum library 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 Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from internal/ethapi/api.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package api
    22  
    23  import (
    24  	"context"
    25  	"crypto/ecdsa"
    26  	"errors"
    27  	"fmt"
    28  	"time"
    29  
    30  	"github.com/klaytn/klaytn/accounts"
    31  	"github.com/klaytn/klaytn/accounts/keystore"
    32  	"github.com/klaytn/klaytn/blockchain/types"
    33  	"github.com/klaytn/klaytn/common"
    34  	"github.com/klaytn/klaytn/common/hexutil"
    35  	"github.com/klaytn/klaytn/common/math"
    36  	"github.com/klaytn/klaytn/crypto"
    37  	"github.com/klaytn/klaytn/rlp"
    38  )
    39  
    40  // PrivateAccountAPI provides an API to access accounts managed by this node.
    41  // It offers methods to create, (un)lock en list accounts. Some methods accept
    42  // passwords and are therefore considered private by default.
    43  type PrivateAccountAPI struct {
    44  	am        accounts.AccountManager
    45  	nonceLock *AddrLocker
    46  	b         Backend
    47  }
    48  
    49  // NewPrivateAccountAPI create a new PrivateAccountAPI.
    50  func NewPrivateAccountAPI(b Backend, nonceLock *AddrLocker) *PrivateAccountAPI {
    51  	return &PrivateAccountAPI{
    52  		am:        b.AccountManager(),
    53  		nonceLock: nonceLock,
    54  		b:         b,
    55  	}
    56  }
    57  
    58  // ListAccounts will return a list of addresses for accounts this node manages.
    59  func (s *PrivateAccountAPI) ListAccounts() []common.Address {
    60  	addresses := make([]common.Address, 0) // return [] instead of nil if empty
    61  	for _, wallet := range s.am.Wallets() {
    62  		for _, account := range wallet.Accounts() {
    63  			addresses = append(addresses, account.Address)
    64  		}
    65  	}
    66  	return addresses
    67  }
    68  
    69  // rawWallet is a JSON representation of an accounts.Wallet interface, with its
    70  // data contents extracted into plain fields.
    71  type rawWallet struct {
    72  	URL      string             `json:"url"`
    73  	Status   string             `json:"status"`
    74  	Failure  string             `json:"failure,omitempty"`
    75  	Accounts []accounts.Account `json:"accounts,omitempty"`
    76  }
    77  
    78  // ListWallets will return a list of wallets this node manages.
    79  func (s *PrivateAccountAPI) ListWallets() []rawWallet {
    80  	wallets := make([]rawWallet, 0) // return [] instead of nil if empty
    81  	for _, wallet := range s.am.Wallets() {
    82  		status, failure := wallet.Status()
    83  
    84  		raw := rawWallet{
    85  			URL:      wallet.URL().String(),
    86  			Status:   status,
    87  			Accounts: wallet.Accounts(),
    88  		}
    89  		if failure != nil {
    90  			raw.Failure = failure.Error()
    91  		}
    92  		wallets = append(wallets, raw)
    93  	}
    94  	return wallets
    95  }
    96  
    97  // OpenWallet initiates a hardware wallet opening procedure, establishing a USB
    98  // connection and attempting to authenticate via the provided passphrase. Note,
    99  // the method may return an extra challenge requiring a second open (e.g. the
   100  // Trezor PIN matrix challenge).
   101  func (s *PrivateAccountAPI) OpenWallet(url string, passphrase *string) error {
   102  	wallet, err := s.am.Wallet(url)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	pass := ""
   107  	if passphrase != nil {
   108  		pass = *passphrase
   109  	}
   110  	return wallet.Open(pass)
   111  }
   112  
   113  // DeriveAccount requests a HD wallet to derive a new account, optionally pinning
   114  // it for later reuse.
   115  func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) {
   116  	wallet, err := s.am.Wallet(url)
   117  	if err != nil {
   118  		return accounts.Account{}, err
   119  	}
   120  	derivPath, err := accounts.ParseDerivationPath(path)
   121  	if err != nil {
   122  		return accounts.Account{}, err
   123  	}
   124  	if pin == nil {
   125  		pin = new(bool)
   126  	}
   127  	return wallet.Derive(derivPath, *pin)
   128  }
   129  
   130  // NewAccount will create a new account and returns the address for the new account.
   131  func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) {
   132  	acc, err := fetchKeystore(s.am).NewAccount(password)
   133  	if err == nil {
   134  		return acc.Address, nil
   135  	}
   136  	return common.Address{}, err
   137  }
   138  
   139  // fetchKeystore retrives the encrypted keystore from the account manager.
   140  func fetchKeystore(am accounts.AccountManager) *keystore.KeyStore {
   141  	return am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   142  }
   143  
   144  func parseKlaytnWalletKey(k string) (string, string, *common.Address, error) {
   145  	// if key length is not 110, just return.
   146  	if len(k) != 110 {
   147  		return k, "", nil, nil
   148  	}
   149  
   150  	walletKeyType := k[66:68]
   151  	if walletKeyType != "00" {
   152  		return "", "", nil, fmt.Errorf("Klaytn wallet key type must be 00.")
   153  	}
   154  	a := common.HexToAddress(k[70:110])
   155  
   156  	return k[0:64], walletKeyType, &a, nil
   157  }
   158  
   159  // ReplaceRawKey stores the given hex encoded ECDSA key into the key directory,
   160  // encrypting it with the passphrase.
   161  func (s *PrivateAccountAPI) ReplaceRawKey(privkey string, passphrase string, newPassphrase string) (common.Address, error) {
   162  	privkey, _, address, err := parseKlaytnWalletKey(privkey)
   163  	if err != nil {
   164  		return common.Address{}, err
   165  	}
   166  	key, err := crypto.HexToECDSA(privkey)
   167  	if err != nil {
   168  		return common.Address{}, err
   169  	}
   170  	acc, err := fetchKeystore(s.am).ReplaceECDSAWithAddress(key, passphrase, newPassphrase, address)
   171  	return acc.Address, err
   172  }
   173  
   174  // ImportRawKey stores the given hex encoded ECDSA key into the key directory,
   175  // encrypting it with the passphrase.
   176  func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) {
   177  	privkey, _, address, err := parseKlaytnWalletKey(privkey)
   178  	if err != nil {
   179  		return common.Address{}, err
   180  	}
   181  	key, err := crypto.HexToECDSA(privkey)
   182  	if err != nil {
   183  		return common.Address{}, err
   184  	}
   185  	acc, err := fetchKeystore(s.am).ImportECDSAWithAddress(key, password, address)
   186  	return acc.Address, err
   187  }
   188  
   189  // UnlockAccount will unlock the account associated with the given address with
   190  // the given password for duration seconds. If duration is nil it will use a
   191  // default of 300 seconds. It returns an indication if the account was unlocked.
   192  func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string, duration *uint64) (bool, error) {
   193  	const max = uint64(time.Duration(math.MaxInt64) / time.Second)
   194  	var d time.Duration
   195  	if duration == nil {
   196  		d = 300 * time.Second
   197  	} else if *duration > max {
   198  		return false, errors.New("unlock duration too large")
   199  	} else {
   200  		d = time.Duration(*duration) * time.Second
   201  	}
   202  	err := fetchKeystore(s.am).TimedUnlock(accounts.Account{Address: addr}, password, d)
   203  	return err == nil, err
   204  }
   205  
   206  // LockAccount will lock the account associated with the given address when it's unlocked.
   207  func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
   208  	return fetchKeystore(s.am).Lock(addr) == nil
   209  }
   210  
   211  // signTransactions sets defaults and signs the given transaction.
   212  // NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
   213  // and release it after the transaction has been submitted to the tx pool.
   214  func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs, passwd string) (*types.Transaction, error) {
   215  	// Look up the wallet containing the requested signer
   216  	account := accounts.Account{Address: args.From}
   217  	wallet, err := s.am.Find(account)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	// Set some sanity defaults and terminate on failure
   222  	if err := args.setDefaults(ctx, s.b); err != nil {
   223  		return nil, err
   224  	}
   225  	// Assemble the transaction and sign with the wallet
   226  	tx, err := args.toTransaction()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	return wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID)
   232  }
   233  
   234  // SendTransaction will create a transaction from the given arguments and try to
   235  // sign it with the key associated with args.From. If the given password isn't
   236  // able to decrypt the key it fails.
   237  func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
   238  	if args.AccountNonce == nil {
   239  		// Hold the addresse's mutex around signing to prevent concurrent assignment of
   240  		// the same nonce to multiple accounts.
   241  		s.nonceLock.LockAddr(args.From)
   242  		defer s.nonceLock.UnlockAddr(args.From)
   243  	}
   244  	signedTx, err := s.SignTransaction(ctx, args, passwd)
   245  	if err != nil {
   246  		return common.Hash{}, err
   247  	}
   248  	return submitTransaction(ctx, s.b, signedTx.Tx)
   249  }
   250  
   251  // SendTransactionAsFeePayer will create a transaction from the given arguments and
   252  // try to sign it as a fee payer with the key associated with args.From. If the
   253  // given password isn't able to decrypt the key it fails.
   254  func (s *PrivateAccountAPI) SendTransactionAsFeePayer(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
   255  	// Don't allow dynamic assign of values from the setDefaults function since the sender already signed on specific values.
   256  	if args.TypeInt == nil {
   257  		return common.Hash{}, errTxArgNilTxType
   258  	}
   259  	if args.AccountNonce == nil {
   260  		return common.Hash{}, errTxArgNilNonce
   261  	}
   262  	if args.GasLimit == nil {
   263  		return common.Hash{}, errTxArgNilGas
   264  	}
   265  	if args.Price == nil {
   266  		return common.Hash{}, errTxArgNilGasPrice
   267  	}
   268  
   269  	if args.TxSignatures == nil {
   270  		return common.Hash{}, errTxArgNilSenderSig
   271  	}
   272  
   273  	feePayerSignedTx, err := s.SignTransactionAsFeePayer(ctx, args, passwd)
   274  	if err != nil {
   275  		return common.Hash{}, err
   276  	}
   277  
   278  	return submitTransaction(ctx, s.b, feePayerSignedTx.Tx)
   279  }
   280  
   281  func (s *PrivateAccountAPI) signNewTransaction(ctx context.Context, args NewTxArgs, passwd string) (*types.Transaction, error) {
   282  	account := accounts.Account{Address: args.from()}
   283  	wallet, err := s.am.Find(account)
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  
   288  	// Set some sanity defaults and terminate on failure
   289  	if err := args.setDefaults(ctx, s.b); err != nil {
   290  		return nil, err
   291  	}
   292  
   293  	tx, err := args.toTransaction()
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  
   298  	signed, err := wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  
   303  	return signed, nil
   304  }
   305  
   306  // SendAccountUpdate will create a TxTypeAccountUpdate transaction from the given arguments and
   307  // try to sign it with the key associated with args.From. If the given password isn't able to
   308  // decrypt the key it fails.
   309  func (s *PrivateAccountAPI) SendAccountUpdate(ctx context.Context, args AccountUpdateTxArgs, passwd string) (common.Hash, error) {
   310  	if args.Nonce == nil {
   311  		// Hold the addresse's mutex around signing to prevent concurrent assignment of
   312  		// the same nonce to multiple accounts.
   313  		s.nonceLock.LockAddr(args.From)
   314  		defer s.nonceLock.UnlockAddr(args.From)
   315  	}
   316  
   317  	signed, err := s.signNewTransaction(ctx, &args, passwd)
   318  	if err != nil {
   319  		return common.Hash{}, err
   320  	}
   321  
   322  	return submitTransaction(ctx, s.b, signed)
   323  }
   324  
   325  // SendValueTransfer will create a TxTypeValueTransfer transaction from the given arguments and
   326  // try to sign it with the key associated with args.From. If the given password isn't able to
   327  // decrypt the key it fails.
   328  func (s *PrivateAccountAPI) SendValueTransfer(ctx context.Context, args ValueTransferTxArgs, passwd string) (common.Hash, error) {
   329  	if args.Nonce == nil {
   330  		// Hold the addresse's mutex around signing to prevent concurrent assignment of
   331  		// the same nonce to multiple accounts.
   332  		s.nonceLock.LockAddr(args.From)
   333  		defer s.nonceLock.UnlockAddr(args.From)
   334  	}
   335  
   336  	signed, err := s.signNewTransaction(ctx, &args, passwd)
   337  	if err != nil {
   338  		return common.Hash{}, err
   339  	}
   340  
   341  	return submitTransaction(ctx, s.b, signed)
   342  }
   343  
   344  // SignTransaction will create a transaction from the given arguments and
   345  // try to sign it with the key associated with args.From. If the given password isn't able to
   346  // decrypt the key, it fails. The transaction is returned in RLP-form, not broadcast to other nodes
   347  func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs, passwd string) (*SignTransactionResult, error) {
   348  	if args.TypeInt != nil && args.TypeInt.IsEthTypedTransaction() {
   349  		if args.Price == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
   350  			return nil, fmt.Errorf("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
   351  		}
   352  	}
   353  
   354  	// No need to obtain the noncelock mutex, since we won't be sending this
   355  	// tx into the transaction pool, but right back to the user
   356  	if err := args.setDefaults(ctx, s.b); err != nil {
   357  		return nil, err
   358  	}
   359  	tx, err := args.toTransaction()
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	signedTx, err := s.sign(args.From, passwd, tx)
   364  	if err != nil {
   365  		return nil, err
   366  	}
   367  	data, err := rlp.EncodeToBytes(signedTx)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  	return &SignTransactionResult{data, signedTx}, nil
   372  }
   373  
   374  // SignTransactionAsFeePayer will create a transaction from the given arguments and
   375  // try to sign it as a fee payer with the key associated with args.From. If the given
   376  // password isn't able to decrypt the key, it fails. The transaction is returned in RLP-form,
   377  // not broadcast to other nodes
   378  func (s *PrivateAccountAPI) SignTransactionAsFeePayer(ctx context.Context, args SendTxArgs, passwd string) (*SignTransactionResult, error) {
   379  	// Allows setting a default nonce value of the sender just for the case the fee payer tries to sign a tx earlier than the sender.
   380  	if err := args.setDefaults(ctx, s.b); err != nil {
   381  		return nil, err
   382  	}
   383  	tx, err := args.toTransaction()
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  	// Don't return errors for nil signature allowing the fee payer to sign a tx earlier than the sender.
   388  	if args.TxSignatures != nil {
   389  		tx.SetSignature(args.TxSignatures.ToTxSignatures())
   390  	}
   391  	feePayer, err := tx.FeePayer()
   392  	if err != nil {
   393  		return nil, errTxArgInvalidFeePayer
   394  	}
   395  	feePayerSignedTx, err := s.signAsFeePayer(feePayer, passwd, tx)
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  	data, err := rlp.EncodeToBytes(feePayerSignedTx)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  	return &SignTransactionResult{data, feePayerSignedTx}, nil
   404  }
   405  
   406  // signHash is a helper function that calculates a hash for the given message that can be
   407  // safely used to calculate a signature from.
   408  //
   409  // The hash is calulcated as
   410  //   keccak256("\x19Klaytn Signed Message:\n"${message length}${message}).
   411  //
   412  // This gives context to the signed message and prevents signing of transactions.
   413  func signHash(data []byte) []byte {
   414  	msg := fmt.Sprintf("\x19Klaytn Signed Message:\n%d%s", len(data), data)
   415  	return crypto.Keccak256([]byte(msg))
   416  }
   417  
   418  func ethSignHash(data []byte) []byte {
   419  	msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
   420  	return crypto.Keccak256([]byte(msg))
   421  }
   422  
   423  // sign is a helper function that signs a transaction with the private key of the given address.
   424  // If the given password isn't able to decrypt the key, it fails.
   425  func (s *PrivateAccountAPI) sign(addr common.Address, passwd string, tx *types.Transaction) (*types.Transaction, error) {
   426  	// Look up the wallet containing the requested signer
   427  	account := accounts.Account{Address: addr}
   428  
   429  	wallet, err := s.b.AccountManager().Find(account)
   430  	if err != nil {
   431  		return nil, err
   432  	}
   433  	// Request the wallet to sign the transaction
   434  	return wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID)
   435  }
   436  
   437  // signAsFeePayer is a helper function that signs a transaction with the private key of the given address.
   438  // If the given password isn't able to decrypt the key, it fails.
   439  func (s *PrivateAccountAPI) signAsFeePayer(addr common.Address, passwd string, tx *types.Transaction) (*types.Transaction, error) {
   440  	// Look up the wallet containing the requested signer
   441  	account := accounts.Account{Address: addr}
   442  
   443  	wallet, err := s.b.AccountManager().Find(account)
   444  	if err != nil {
   445  		return nil, err
   446  	}
   447  	// Request the wallet to sign the transaction
   448  	return wallet.SignTxAsFeePayerWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID)
   449  }
   450  
   451  // Sign calculates a Klaytn ECDSA signature for:
   452  // keccack256("\x19Klaytn Signed Message:\n" + len(message) + message))
   453  //
   454  // Note, the produced signature conforms to the secp256k1 curve R, S and V values,
   455  // where the V value will be 27 or 28 for legacy reasons.
   456  //
   457  // The key used to calculate the signature is decrypted with the given password.
   458  //
   459  // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
   460  func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
   461  	// Look up the wallet containing the requested signer
   462  	account := accounts.Account{Address: addr}
   463  
   464  	wallet, err := s.b.AccountManager().Find(account)
   465  	if err != nil {
   466  		return nil, err
   467  	}
   468  	// Assemble sign the data with the wallet
   469  	signature, err := wallet.SignHashWithPassphrase(account, passwd, signHash(data))
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  	signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
   474  	return signature, nil
   475  }
   476  
   477  // EcRecover returns the address for the account that was used to create the signature.
   478  // Note, this function is compatible with eth_sign and personal_sign. As such it recovers
   479  // the address of:
   480  // hash = keccak256("\x19Klaytn Signed Message:\n"${message length}${message})
   481  // addr = ecrecover(hash, signature)
   482  //
   483  // Note, the signature must conform to the secp256k1 curve R, S and V values, where
   484  // the V value must be 27 or 28 for legacy reasons.
   485  //
   486  // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
   487  func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
   488  	if len(sig) != crypto.SignatureLength {
   489  		return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
   490  	}
   491  	if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 {
   492  		return common.Address{}, fmt.Errorf("invalid Klaytn signature (V is not 27 or 28)")
   493  	}
   494  
   495  	// Transform yellow paper V from 27/28 to 0/1
   496  	sig[crypto.RecoveryIDOffset] -= 27
   497  
   498  	pubkey, err := klayEcRecover(data, sig)
   499  	if err != nil {
   500  		return common.Address{}, err
   501  	}
   502  	return crypto.PubkeyToAddress(*pubkey), nil
   503  }
   504  
   505  func klayEcRecover(data, sig hexutil.Bytes) (*ecdsa.PublicKey, error) {
   506  	return crypto.SigToPub(signHash(data), sig)
   507  }
   508  
   509  func ethEcRecover(data, sig hexutil.Bytes) (*ecdsa.PublicKey, error) {
   510  	return crypto.SigToPub(ethSignHash(data), sig)
   511  }
   512  
   513  // SignAndSendTransaction was renamed to SendTransaction. This method is deprecated
   514  // and will be removed in the future. It primary goal is to give clients time to update.
   515  func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
   516  	return s.SendTransaction(ctx, args, passwd)
   517  }