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  }