github.com/klaytn/klaytn@v1.10.2/node/sc/bridge_accounts.go (about) 1 // Copyright 2019 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package sc 18 19 import ( 20 "errors" 21 "io/ioutil" 22 "math" 23 "math/big" 24 "os" 25 "path" 26 "sync" 27 "time" 28 29 "github.com/klaytn/klaytn/accounts" 30 "github.com/klaytn/klaytn/accounts/abi/bind" 31 "github.com/klaytn/klaytn/accounts/keystore" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/cmd/homi/setup" 34 "github.com/klaytn/klaytn/common" 35 "github.com/klaytn/klaytn/params" 36 ) 37 38 const ( 39 ParentOperatorStr = "parentOperator" 40 ChildOperatorStr = "childOperator" 41 ParentBridgeAccountName = "parent_bridge_account" 42 ChildBridgeAccountName = "child_bridge_account" 43 ) 44 45 var errUnlockDurationTooLarge = errors.New("unlock duration too large") 46 47 type feePayerDB interface { 48 WriteParentOperatorFeePayer(feePayer common.Address) 49 WriteChildOperatorFeePayer(feePayer common.Address) 50 ReadParentOperatorFeePayer() common.Address 51 ReadChildOperatorFeePayer() common.Address 52 } 53 54 // accountInfo has bridge account's information to make and sign a transaction. 55 type accountInfo struct { 56 am *accounts.Manager // the account manager of the node for the fee payer. 57 keystore *keystore.KeyStore // the keystore of the operator. 58 address common.Address 59 nonce uint64 60 chainID *big.Int 61 gasPrice *big.Int 62 gasLimit uint64 63 kip71Config params.KIP71Config 64 65 isNonceSynced bool 66 mu sync.RWMutex 67 68 feePayer common.Address 69 } 70 71 // BridgeAccounts manages bridge account for parent/child chain. 72 type BridgeAccounts struct { 73 pAccount *accountInfo 74 cAccount *accountInfo 75 db feePayerDB 76 } 77 78 // GetBridgeOperators returns the information of bridgeOperator. 79 func (ba *BridgeAccounts) GetBridgeOperators() map[string]interface{} { 80 res := make(map[string]interface{}) 81 82 res[ParentOperatorStr] = ba.pAccount.GetAccountInfo() 83 res[ChildOperatorStr] = ba.cAccount.GetAccountInfo() 84 85 return res 86 } 87 88 // GetParentOperatorFeePayer can return the fee payer of parent operator. 89 func (ba *BridgeAccounts) GetParentOperatorFeePayer() common.Address { 90 return ba.pAccount.feePayer 91 } 92 93 // SetParentOperatorFeePayer can set the fee payer of parent operator. 94 func (ba *BridgeAccounts) SetParentOperatorFeePayer(feePayer common.Address) error { 95 ba.pAccount.feePayer = feePayer 96 ba.db.WriteParentOperatorFeePayer(feePayer) 97 return nil 98 } 99 100 // GetChildOperatorFeePayer can return the fee payer of child operator. 101 func (ba *BridgeAccounts) GetChildOperatorFeePayer() common.Address { 102 return ba.cAccount.feePayer 103 } 104 105 // SetChildOperatorFeePayer can set the fee payer of child operator. 106 func (ba *BridgeAccounts) SetChildOperatorFeePayer(feePayer common.Address) error { 107 ba.cAccount.feePayer = feePayer 108 ba.db.WriteChildOperatorFeePayer(feePayer) 109 return nil 110 } 111 112 // GetParentBridgeOperatorGasLimit gets value of GasLimit of parent operator. 113 func (ba *BridgeAccounts) GetParentBridgeOperatorGasLimit() uint64 { 114 return ba.pAccount.gasLimit 115 } 116 117 // GetChildBridgeOperatorGasLimit gets value of GasLimit of child operator. 118 func (ba *BridgeAccounts) GetChildBridgeOperatorGasLimit() uint64 { 119 return ba.cAccount.gasLimit 120 } 121 122 // SetParentBridgeOperatorGasLimit changes GasLimit of parent operator. 123 func (ba *BridgeAccounts) SetParentBridgeOperatorGasLimit(fee uint64) { 124 ba.pAccount.gasLimit = fee 125 } 126 127 // SetChildBridgeOperatorGasLimit changes GasLimit of child operator. 128 func (ba *BridgeAccounts) SetChildBridgeOperatorGasLimit(fee uint64) { 129 ba.cAccount.gasLimit = fee 130 } 131 132 // GetParentGasPrice returns the parent chain's gas price. 133 func (ba *BridgeAccounts) GetParentGasPrice() uint64 { 134 if ba.pAccount.gasPrice == nil { 135 return 0 136 } 137 return ba.pAccount.gasPrice.Uint64() 138 } 139 140 func (ba *BridgeAccounts) SetParentKIP71Config(kip71Config params.KIP71Config) { 141 ba.pAccount.kip71Config = kip71Config 142 } 143 144 func (ba *BridgeAccounts) GetParentKIP71Config() params.KIP71Config { 145 return ba.pAccount.kip71Config 146 } 147 148 // NewBridgeAccounts returns bridgeAccounts created by main/service bridge account keys. 149 func NewBridgeAccounts(am *accounts.Manager, dataDir string, db feePayerDB, parentOperatorGaslimit, childOperatorGaslimit uint64) (*BridgeAccounts, error) { 150 pKS, pAccAddr, isLock, err := InitializeBridgeAccountKeystore(path.Join(dataDir, ParentBridgeAccountName)) 151 if err != nil { 152 return nil, err 153 } 154 155 if isLock { 156 logger.Warn("parent bridge account is locked. Please unlock the account manually for Service Chain", "name", ParentBridgeAccountName) 157 } 158 159 cKS, cAccAddr, isLock, err := InitializeBridgeAccountKeystore(path.Join(dataDir, ChildBridgeAccountName)) 160 if err != nil { 161 return nil, err 162 } 163 164 if isLock { 165 logger.Warn("child bridge account is locked. Please unlock the account manually for Service Chain", "name", ChildBridgeAccountName) 166 } 167 168 logger.Info("bridge account is loaded", "parent", pAccAddr.String(), "child", cAccAddr.String()) 169 170 pAccInfo := &accountInfo{ 171 am: am, 172 keystore: pKS, 173 address: pAccAddr, 174 nonce: 0, 175 chainID: nil, 176 gasPrice: nil, 177 gasLimit: parentOperatorGaslimit, 178 feePayer: db.ReadParentOperatorFeePayer(), 179 } 180 181 cAccInfo := &accountInfo{ 182 am: am, 183 keystore: cKS, 184 address: cAccAddr, 185 nonce: 0, 186 chainID: nil, 187 gasPrice: nil, 188 gasLimit: childOperatorGaslimit, 189 feePayer: db.ReadChildOperatorFeePayer(), 190 } 191 192 return &BridgeAccounts{ 193 pAccount: pAccInfo, 194 cAccount: cAccInfo, 195 db: db, 196 }, nil 197 } 198 199 // InitializeBridgeAccountKeystore initializes a keystore, imports existing keys, and tries to unlock the bridge account. 200 // This returns the 1st account of the wallet, its address, the lock status and the error. 201 func InitializeBridgeAccountKeystore(keystorePath string) (*keystore.KeyStore, common.Address, bool, error) { 202 ks := keystore.NewKeyStore(keystorePath, keystore.StandardScryptN, keystore.StandardScryptP) 203 204 // If there is no keystore file, this creates a random account and the corresponded password file. 205 // TODO-Klaytn-Servicechain A test-option will be added and this routine will be only executed with it. 206 if len(ks.Accounts()) == 0 { 207 password := setup.RandStringRunes(params.PasswordLength) 208 acc, err := ks.NewAccount(password) 209 if err != nil { 210 return nil, common.Address{}, true, err 211 } 212 setup.WriteFile([]byte(password), keystorePath, acc.Address.String()) 213 214 if err := ks.Unlock(acc, password); err != nil { 215 logger.Error("bridge account wallet unlock is failed by created password file.", "address", acc.Address, "err", err) 216 os.RemoveAll(keystorePath) 217 return nil, common.Address{}, true, err 218 } 219 220 return ks, acc.Address, false, nil 221 } 222 223 // Try to unlock 1st account if valid password file exist. (optional behavior) 224 // If unlocking failed, user should unlock it through API. 225 acc := ks.Accounts()[0] 226 pwdFilePath := path.Join(keystorePath, acc.Address.String()) 227 pwdStr, err := ioutil.ReadFile(pwdFilePath) 228 if err == nil { 229 if err := ks.Unlock(acc, string(pwdStr)); err != nil { 230 logger.Warn("bridge account wallet unlock is failed by exist password file.", "address", acc.Address, "err", err) 231 return ks, acc.Address, true, nil 232 } 233 return ks, acc.Address, false, nil 234 } 235 236 return ks, acc.Address, true, nil 237 } 238 239 // GetAccountInfo returns the information of the account. 240 func (acc *accountInfo) GetAccountInfo() map[string]interface{} { 241 res := make(map[string]interface{}) 242 243 res["address"] = acc.address 244 res["nonce"] = acc.nonce 245 res["isNonceSynced"] = acc.isNonceSynced 246 res["isUnlocked"] = acc.IsUnlockedAccount() 247 res["chainID"] = acc.chainID 248 res["gasPrice"] = acc.gasPrice 249 250 return res 251 } 252 253 // GenerateTransactOpts returns a transactOpts for transact on local/remote backend. 254 func (acc *accountInfo) GenerateTransactOpts() *bind.TransactOpts { 255 var nonce *big.Int 256 257 // Only for unit test, if the nonce is not synced yet, return transaction option with nil nonce. 258 // Backend will use state nonce. 259 if acc.isNonceSynced { 260 nonce = new(big.Int).SetUint64(acc.nonce) 261 } 262 263 gasPrice := acc.gasPrice 264 if acc.kip71Config.UpperBoundBaseFee != 0 { 265 gasPrice = new(big.Int).SetUint64(acc.kip71Config.UpperBoundBaseFee) 266 } 267 268 return bind.MakeTransactOptsWithKeystore(acc.keystore, acc.address, nonce, acc.chainID, acc.gasLimit, gasPrice) 269 } 270 271 // SignTx signs a transaction with the accountInfo. 272 func (acc *accountInfo) SignTx(tx *types.Transaction) (*types.Transaction, error) { 273 tx, err := acc.keystore.SignTx(accounts.Account{Address: acc.address}, tx, acc.chainID) 274 if err != nil { 275 return nil, err 276 } 277 278 if tx.Type().IsFeeDelegatedTransaction() { 279 // Look up the wallet containing the requested signer 280 account := accounts.Account{Address: acc.feePayer} 281 282 wallet, err := acc.am.Find(account) 283 if err != nil { 284 return nil, err 285 } 286 // Request the wallet to sign the transaction 287 return wallet.SignTxAsFeePayer(account, tx, acc.chainID) 288 } 289 return tx, nil 290 } 291 292 // SetChainID sets the chain ID of the chain of the account. 293 func (acc *accountInfo) SetChainID(cID *big.Int) { 294 acc.chainID = cID 295 } 296 297 // TODO-Servicechain: Remove `SetGasPrice` function once Magma-fork is done. 298 // SetGasPrice sets the gas price of the chain of the account. 299 func (acc *accountInfo) SetGasPrice(gp *big.Int) { 300 acc.gasPrice = gp 301 } 302 303 // Lock can lock the account for nonce management. 304 func (acc *accountInfo) Lock() { 305 acc.mu.Lock() 306 } 307 308 // UnLock can unlock the account for nonce management. 309 func (acc *accountInfo) UnLock() { 310 acc.mu.Unlock() 311 } 312 313 // SetNonce can set the nonce of the account. 314 func (acc *accountInfo) SetNonce(n uint64) { 315 acc.nonce = n 316 acc.isNonceSynced = true 317 } 318 319 // GetNonce can return the nonce of the account. 320 func (acc *accountInfo) GetNonce() uint64 { 321 return acc.nonce 322 } 323 324 // IncNonce can increase the nonce of the account. 325 func (acc *accountInfo) IncNonce() { 326 acc.nonce++ 327 } 328 329 // LockAccount can lock the account keystore. 330 func (acc *accountInfo) LockAccount() error { 331 acc.mu.Lock() 332 defer acc.mu.Unlock() 333 334 if err := acc.keystore.Lock(acc.address); err != nil { 335 logger.Error("Failed to lock the account", "account", acc.address) 336 return err 337 } 338 logger.Info("Succeed to lock the account", "account", acc.address) 339 return nil 340 } 341 342 // UnLockAccount can unlock the account keystore. 343 func (acc *accountInfo) UnLockAccount(passphrase string, duration *uint64) error { 344 acc.mu.Lock() 345 defer acc.mu.Unlock() 346 347 const max = uint64(time.Duration(math.MaxInt64) / time.Second) 348 var d time.Duration 349 if duration == nil { 350 d = 0 351 } else if *duration > max { 352 return errUnlockDurationTooLarge 353 } else { 354 d = time.Duration(*duration) * time.Second 355 } 356 357 if err := acc.keystore.TimedUnlock(acc.keystore.Accounts()[0], passphrase, d); err != nil { 358 logger.Error("Failed to unlock the account", "account", acc.address) 359 return err 360 } 361 logger.Info("Succeed to unlock the account", "account", acc.address) 362 return nil 363 } 364 365 // IsUnlockedAccount can return if the account is unlocked or not. 366 func (acc *accountInfo) IsUnlockedAccount() bool { 367 acc.mu.Lock() 368 defer acc.mu.Unlock() 369 return acc.keystore.IsUnlocked(acc.address) 370 }