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 }