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