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  }