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 }