github.com/klaytn/klaytn@v1.10.2/node/sc/local_backend.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 accounts/abi/bind/backends/simulated.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package sc
    22  
    23  import (
    24  	"context"
    25  	"errors"
    26  	"fmt"
    27  	"math/big"
    28  	"time"
    29  
    30  	"github.com/klaytn/klaytn"
    31  	"github.com/klaytn/klaytn/blockchain"
    32  	"github.com/klaytn/klaytn/blockchain/bloombits"
    33  	"github.com/klaytn/klaytn/blockchain/state"
    34  	"github.com/klaytn/klaytn/blockchain/types"
    35  	"github.com/klaytn/klaytn/blockchain/vm"
    36  	"github.com/klaytn/klaytn/common"
    37  	"github.com/klaytn/klaytn/common/math"
    38  	"github.com/klaytn/klaytn/event"
    39  	"github.com/klaytn/klaytn/networks/rpc"
    40  	"github.com/klaytn/klaytn/node/cn/filters"
    41  	"github.com/klaytn/klaytn/params"
    42  	"github.com/klaytn/klaytn/storage/database"
    43  )
    44  
    45  const defaultGasPrice = 50 * params.Ston
    46  
    47  var (
    48  	errBlockNumberUnsupported = errors.New("LocalBackend cannot access blocks other than the latest block")
    49  	errGasEstimationFailed    = errors.New("gas required exceeds allowance or always failing transaction")
    50  )
    51  
    52  // TODO-Klaytn currently LocalBackend is only for ServiceChain, especially Bridge SmartContract
    53  type LocalBackend struct {
    54  	subbridge *SubBridge
    55  
    56  	events *filters.EventSystem // Event system for filtering log events live
    57  	config *params.ChainConfig
    58  }
    59  
    60  func checkCtx(ctx context.Context) error {
    61  	select {
    62  	case <-ctx.Done():
    63  		return ctx.Err()
    64  	default:
    65  		return nil
    66  	}
    67  }
    68  
    69  func NewLocalBackend(main *SubBridge) (*LocalBackend, error) {
    70  	return &LocalBackend{
    71  		subbridge: main,
    72  		config:    main.blockchain.Config(),
    73  		events:    filters.NewEventSystem(main.EventMux(), &filterLocalBackend{main}, false),
    74  	}, nil
    75  }
    76  
    77  func (lb *LocalBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
    78  	if err := checkCtx(ctx); err != nil {
    79  		return nil, err
    80  	}
    81  	if blockNumber != nil && blockNumber.Cmp(lb.subbridge.blockchain.CurrentBlock().Number()) != 0 {
    82  		return nil, errBlockNumberUnsupported
    83  	}
    84  	statedb, err := lb.subbridge.blockchain.State()
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	return statedb.GetCode(contract), nil
    89  }
    90  
    91  func (lb *LocalBackend) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
    92  	if err := checkCtx(ctx); err != nil {
    93  		return nil, err
    94  	}
    95  	if blockNumber != nil && blockNumber.Cmp(lb.subbridge.blockchain.CurrentBlock().Number()) != 0 {
    96  		return nil, errBlockNumberUnsupported
    97  	}
    98  	statedb, err := lb.subbridge.blockchain.State()
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return statedb.GetBalance(account), nil
   103  }
   104  
   105  func (lb *LocalBackend) CallContract(ctx context.Context, call klaytn.CallMsg, blockNumber *big.Int) ([]byte, error) {
   106  	if err := checkCtx(ctx); err != nil {
   107  		return nil, err
   108  	}
   109  	if blockNumber != nil && blockNumber.Cmp(lb.subbridge.blockchain.CurrentBlock().Number()) != 0 {
   110  		return nil, errBlockNumberUnsupported
   111  	}
   112  	currentState, err := lb.subbridge.blockchain.State()
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	rval, _, _, err := lb.callContract(ctx, call, lb.subbridge.blockchain.CurrentBlock(), currentState)
   117  	return rval, err
   118  }
   119  
   120  func (b *LocalBackend) callContract(ctx context.Context, call klaytn.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
   121  	// Set default gas & gas price if none were set
   122  	gas, gasPrice := uint64(call.Gas), call.GasPrice
   123  	if gas == 0 {
   124  		gas = math.MaxUint64 / 2
   125  	}
   126  	if gasPrice == nil || gasPrice.Sign() == 0 {
   127  		gasPrice = new(big.Int).SetUint64(defaultGasPrice)
   128  	}
   129  
   130  	intrinsicGas, err := types.IntrinsicGas(call.Data, nil, call.To == nil, b.config.Rules(block.Number()))
   131  	if err != nil {
   132  		return nil, 0, false, err
   133  	}
   134  
   135  	// Create new call message
   136  	msg := types.NewMessage(call.From, call.To, 0, call.Value, gas, gasPrice, call.Data, false, intrinsicGas)
   137  
   138  	// Setup context so it may be cancelled the call has completed
   139  	// or, in case of unmetered gas, setup a context with a timeout.
   140  	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
   141  
   142  	// Make sure the context is cancelled when the call has completed
   143  	// this makes sure resources are cleaned up.
   144  	defer cancel()
   145  
   146  	statedb.SetBalance(msg.ValidatedSender(), math.MaxBig256)
   147  	vmError := func() error { return nil }
   148  
   149  	context := blockchain.NewEVMContext(msg, block.Header(), b.subbridge.blockchain, nil)
   150  	evm := vm.NewEVM(context, statedb, b.config, &vm.Config{})
   151  	// Wait for the context to be done and cancel the evm. Even if the
   152  	// EVM has finished, cancelling may be done (repeatedly)
   153  	go func() {
   154  		<-ctx.Done()
   155  		evm.Cancel(vm.CancelByCtxDone)
   156  	}()
   157  
   158  	res, gas, kerr := blockchain.ApplyMessage(evm, msg)
   159  	err = kerr.ErrTxInvalid
   160  	if err := vmError(); err != nil {
   161  		return nil, 0, false, err
   162  	}
   163  
   164  	// Propagate error of Receipt as JSON RPC error
   165  	if err == nil {
   166  		err = blockchain.GetVMerrFromReceiptStatus(kerr.Status)
   167  	}
   168  
   169  	return res, gas, kerr.Status != types.ReceiptStatusSuccessful, err
   170  }
   171  
   172  func (lb *LocalBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
   173  	if err := checkCtx(ctx); err != nil {
   174  		return nil, err
   175  	}
   176  	// TODO-Klaytn this is not pending code but latest code
   177  	return lb.CodeAt(ctx, contract, lb.subbridge.blockchain.CurrentBlock().Number())
   178  }
   179  
   180  func (lb *LocalBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
   181  	if err := checkCtx(ctx); err != nil {
   182  		return 0, err
   183  	}
   184  	return lb.subbridge.txPool.GetPendingNonce(account), nil
   185  }
   186  
   187  func (lb *LocalBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
   188  	if err := checkCtx(ctx); err != nil {
   189  		return nil, err
   190  	}
   191  	if lb.subbridge.blockchain.Config().IsMagmaForkEnabled(lb.subbridge.blockchain.CurrentHeader().Number) {
   192  		return new(big.Int).SetUint64(lb.config.Governance.KIP71.UpperBoundBaseFee), nil
   193  	} else {
   194  		return new(big.Int).SetUint64(lb.config.UnitPrice), nil
   195  	}
   196  }
   197  
   198  func (lb *LocalBackend) EstimateGas(ctx context.Context, call klaytn.CallMsg) (gas uint64, err error) {
   199  	if err := checkCtx(ctx); err != nil {
   200  		return 0, err
   201  	}
   202  	// Binary search the gas requirement, as it may be higher than the amount used
   203  	var (
   204  		lo  uint64 = params.TxGas - 1
   205  		hi  uint64
   206  		cap uint64
   207  	)
   208  	if uint64(call.Gas) >= params.TxGas {
   209  		hi = uint64(call.Gas)
   210  	} else {
   211  		hi = params.UpperGasLimit
   212  	}
   213  	cap = hi
   214  
   215  	// Create a helper to check if a gas allowance results in an executable transaction
   216  	executable := func(gas uint64) bool {
   217  		call.Gas = gas
   218  
   219  		currentState, err := lb.subbridge.blockchain.State()
   220  		if err != nil {
   221  			return false
   222  		}
   223  		_, _, failed, err := lb.callContract(ctx, call, lb.subbridge.blockchain.CurrentBlock(), currentState)
   224  		if err != nil || failed {
   225  			return false
   226  		}
   227  		return true
   228  	}
   229  	// Execute the binary search and hone in on an executable gas limit
   230  	for lo+1 < hi {
   231  		mid := (hi + lo) / 2
   232  		if !executable(mid) {
   233  			lo = mid
   234  		} else {
   235  			hi = mid
   236  		}
   237  	}
   238  	// Reject the transaction as invalid if it still fails at the highest allowance
   239  	if hi == cap {
   240  		if !executable(hi) {
   241  			return 0, fmt.Errorf("gas required exceeds allowance or always failing transaction")
   242  		}
   243  	}
   244  	return hi, nil
   245  }
   246  
   247  func (lb *LocalBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
   248  	if err := checkCtx(ctx); err != nil {
   249  		return err
   250  	}
   251  	return lb.subbridge.txPool.AddLocal(tx)
   252  }
   253  
   254  // ChainID can return the chain ID of the chain.
   255  func (lb *LocalBackend) ChainID(ctx context.Context) (*big.Int, error) {
   256  	if err := checkCtx(ctx); err != nil {
   257  		return nil, err
   258  	}
   259  	return lb.config.ChainID, nil
   260  }
   261  
   262  func (lb *LocalBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
   263  	if err := checkCtx(ctx); err != nil {
   264  		return nil, err
   265  	}
   266  	receipt := lb.subbridge.blockchain.GetReceiptByTxHash(txHash)
   267  	if receipt != nil {
   268  		return receipt, nil
   269  	}
   270  	return nil, errors.New("receipt is not exist")
   271  }
   272  
   273  func (lb *LocalBackend) FilterLogs(ctx context.Context, query klaytn.FilterQuery) ([]types.Log, error) {
   274  	if err := checkCtx(ctx); err != nil {
   275  		return nil, err
   276  	}
   277  	// Convert the RPC block numbers into internal representations
   278  	if query.FromBlock == nil {
   279  		query.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
   280  	}
   281  	if query.ToBlock == nil {
   282  		query.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
   283  	}
   284  	from := query.FromBlock.Int64()
   285  	to := query.ToBlock.Int64()
   286  
   287  	// Construct and execute the filter
   288  	filter := filters.NewRangeFilter(&filterLocalBackend{lb.subbridge}, from, to, query.Addresses, query.Topics)
   289  
   290  	logs, err := filter.Logs(ctx)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	res := make([]types.Log, len(logs))
   295  	for i, log := range logs {
   296  		res[i] = *log
   297  	}
   298  	return res, nil
   299  }
   300  
   301  func (lb *LocalBackend) SubscribeFilterLogs(ctx context.Context, query klaytn.FilterQuery, ch chan<- types.Log) (klaytn.Subscription, error) {
   302  	if err := checkCtx(ctx); err != nil {
   303  		return nil, err
   304  	}
   305  	// Subscribe to contract events
   306  	sink := make(chan []*types.Log)
   307  
   308  	sub, err := lb.events.SubscribeLogs(query, sink)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	// Since we're getting logs in batches, we need to flatten them into a plain stream
   313  	return event.NewSubscription(func(quit <-chan struct{}) error {
   314  		defer sub.Unsubscribe()
   315  		for {
   316  			select {
   317  			case logs := <-sink:
   318  				for _, log := range logs {
   319  					select {
   320  					case ch <- *log:
   321  					case err := <-sub.Err():
   322  						return err
   323  					case <-quit:
   324  						return nil
   325  					}
   326  				}
   327  			case err := <-sub.Err():
   328  				return err
   329  			case <-quit:
   330  				return nil
   331  			}
   332  		}
   333  	}), nil
   334  }
   335  
   336  // CurrentBlockNumber returns a current block number.
   337  func (lb *LocalBackend) CurrentBlockNumber(ctx context.Context) (uint64, error) {
   338  	if err := checkCtx(ctx); err != nil {
   339  		return 0, err
   340  	}
   341  	return lb.subbridge.blockchain.CurrentBlock().NumberU64(), nil
   342  }
   343  
   344  type filterLocalBackend struct {
   345  	subbridge *SubBridge
   346  }
   347  
   348  func (fb *filterLocalBackend) ChainDB() database.DBManager {
   349  	// TODO-Klaytn consider chain's chainDB instead of bridge's chainDB currently.
   350  	return fb.subbridge.chainDB
   351  }
   352  
   353  func (fb *filterLocalBackend) EventMux() *event.TypeMux {
   354  	// TODO-Klaytn consider chain's eventMux instead of bridge's eventMux currently.
   355  	return fb.subbridge.EventMux()
   356  }
   357  
   358  func (fb *filterLocalBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
   359  	if header := fb.subbridge.blockchain.GetHeaderByHash(hash); header != nil {
   360  		return header, nil
   361  	}
   362  	return nil, fmt.Errorf("the header does not exist (hash: %d)", hash)
   363  }
   364  
   365  func (fb *filterLocalBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
   366  	if err := checkCtx(ctx); err != nil {
   367  		return nil, err
   368  	}
   369  	// TODO-Klaytn consider pendingblock instead of latest block
   370  	if block == rpc.LatestBlockNumber {
   371  		return fb.subbridge.blockchain.CurrentHeader(), nil
   372  	}
   373  	return fb.subbridge.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
   374  }
   375  
   376  func (fb *filterLocalBackend) GetBlockReceipts(ctx context.Context, hash common.Hash) types.Receipts {
   377  	if err := checkCtx(ctx); err != nil {
   378  		return nil
   379  	}
   380  	return fb.subbridge.blockchain.GetReceiptsByBlockHash(hash)
   381  }
   382  
   383  func (fb *filterLocalBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
   384  	if err := checkCtx(ctx); err != nil {
   385  		return nil, err
   386  	}
   387  	return fb.subbridge.blockchain.GetLogsByHash(hash), nil
   388  }
   389  
   390  func (fb *filterLocalBackend) SubscribeNewTxsEvent(ch chan<- blockchain.NewTxsEvent) event.Subscription {
   391  	return fb.subbridge.txPool.SubscribeNewTxsEvent(ch)
   392  }
   393  
   394  func (fb *filterLocalBackend) SubscribeChainEvent(ch chan<- blockchain.ChainEvent) event.Subscription {
   395  	return fb.subbridge.blockchain.SubscribeChainEvent(ch)
   396  }
   397  
   398  func (fb *filterLocalBackend) SubscribeRemovedLogsEvent(ch chan<- blockchain.RemovedLogsEvent) event.Subscription {
   399  	return fb.subbridge.blockchain.SubscribeRemovedLogsEvent(ch)
   400  }
   401  
   402  func (fb *filterLocalBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
   403  	return fb.subbridge.blockchain.SubscribeLogsEvent(ch)
   404  }
   405  
   406  func (fb *filterLocalBackend) BloomStatus() (uint64, uint64) {
   407  	// TODO-Klaytn consider this number of sections.
   408  	// BloomBitsBlocks (const : 4096), the number of processed sections maintained by the chain indexer
   409  	return 4096, 0
   410  }
   411  
   412  func (fb *filterLocalBackend) ServiceFilter(_dummyCtx context.Context, session *bloombits.MatcherSession) {
   413  	// TODO-Klaytn this method should implmentation to support indexed tag in solidity
   414  	//for i := 0; i < bloomFilterThreads; i++ {
   415  	//	go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, backend.bloomRequests)
   416  	//}
   417  }
   418  
   419  func (fb *filterLocalBackend) ChainConfig() *params.ChainConfig {
   420  	return fb.subbridge.blockchain.Config()
   421  }