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