github.com/klaytn/klaytn@v1.12.1/node/sc/remote_backend.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  	"context"
    21  	"math/big"
    22  	"net"
    23  	"time"
    24  
    25  	"github.com/klaytn/klaytn"
    26  	"github.com/klaytn/klaytn/blockchain/types"
    27  	"github.com/klaytn/klaytn/common"
    28  	"github.com/klaytn/klaytn/common/hexutil"
    29  	"github.com/klaytn/klaytn/networks/rpc"
    30  	"github.com/pkg/errors"
    31  )
    32  
    33  var NoParentPeerErr = errors.New("no parent peer")
    34  
    35  const timeout = 30 * time.Second
    36  
    37  // TODO-Klaytn currently RemoteBackend is only for ServiceChain, especially Bridge SmartContract
    38  type RemoteBackend struct {
    39  	subBridge *SubBridge
    40  
    41  	rpcClient *rpc.Client
    42  	chainID   *big.Int
    43  }
    44  
    45  func NewRpcClientP2P(sb *SubBridge) *rpc.Client {
    46  	initctx := context.Background()
    47  	c, _ := rpc.NewClient(initctx, func(ctx context.Context) (rpc.ServerCodec, error) {
    48  		p1, p2 := net.Pipe()
    49  		sb.SetRPCConn(p1)
    50  		return rpc.NewCodec(p2), nil
    51  	})
    52  	return c
    53  }
    54  
    55  func NewRemoteBackend(sb *SubBridge) (*RemoteBackend, error) {
    56  	rCli := NewRpcClientP2P(sb)
    57  
    58  	return &RemoteBackend{
    59  		subBridge: sb,
    60  		rpcClient: rCli,
    61  	}, nil
    62  }
    63  
    64  func (rb *RemoteBackend) checkParentPeer() bool {
    65  	return rb.subBridge.peers.Len() > 0
    66  }
    67  
    68  func (rb *RemoteBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
    69  	if !rb.checkParentPeer() {
    70  		return nil, NoParentPeerErr
    71  	}
    72  	var result hexutil.Bytes
    73  	err := rb.rpcClient.CallContext(ctx, &result, "klay_getCode", contract, toBlockNumArg(blockNumber))
    74  	return result, err
    75  }
    76  
    77  func (rb *RemoteBackend) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
    78  	if !rb.checkParentPeer() {
    79  		return nil, NoParentPeerErr
    80  	}
    81  	var hex hexutil.Big
    82  	err := rb.rpcClient.CallContext(ctx, &hex, "klay_getBalance", account, toBlockNumArg(blockNumber))
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	return (*big.Int)(&hex), nil
    87  }
    88  
    89  func (rb *RemoteBackend) CallContract(ctx context.Context, call klaytn.CallMsg, blockNumber *big.Int) ([]byte, error) {
    90  	if !rb.checkParentPeer() {
    91  		return nil, NoParentPeerErr
    92  	}
    93  	var hex hexutil.Bytes
    94  	err := rb.rpcClient.CallContext(ctx, &hex, "klay_call", toCallArg(call), toBlockNumArg(blockNumber))
    95  	return hex, err
    96  }
    97  
    98  func (rb *RemoteBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
    99  	if !rb.checkParentPeer() {
   100  		return nil, NoParentPeerErr
   101  	}
   102  	var result hexutil.Bytes
   103  	err := rb.rpcClient.CallContext(ctx, &result, "klay_getCode", contract, "pending")
   104  	return result, err
   105  }
   106  
   107  func (rb *RemoteBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
   108  	if !rb.checkParentPeer() {
   109  		return 0, NoParentPeerErr
   110  	}
   111  	var result hexutil.Uint64
   112  	err := rb.rpcClient.CallContext(ctx, &result, "klay_getTransactionCount", account, "pending")
   113  	return uint64(result), err
   114  }
   115  
   116  func (rb *RemoteBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
   117  	if !rb.checkParentPeer() {
   118  		return nil, NoParentPeerErr
   119  	}
   120  	var hex hexutil.Big
   121  	if err := rb.rpcClient.CallContext(ctx, &hex, "klay_gasPrice"); err != nil {
   122  		return nil, err
   123  	}
   124  	return (*big.Int)(&hex), nil
   125  }
   126  
   127  func (rb *RemoteBackend) EstimateGas(ctx context.Context, msg klaytn.CallMsg) (uint64, error) {
   128  	if !rb.checkParentPeer() {
   129  		return 0, NoParentPeerErr
   130  	}
   131  
   132  	var hex hexutil.Uint64
   133  	err := rb.rpcClient.CallContext(ctx, &hex, "klay_estimateGas", toCallArg(msg))
   134  	if err != nil {
   135  		return 0, err
   136  	}
   137  	return uint64(hex), nil
   138  }
   139  
   140  func (rb *RemoteBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
   141  	if !rb.checkParentPeer() {
   142  		return NoParentPeerErr
   143  	}
   144  	return rb.subBridge.bridgeTxPool.AddLocal(tx)
   145  }
   146  
   147  func (rb *RemoteBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
   148  	if !rb.checkParentPeer() {
   149  		return nil, NoParentPeerErr
   150  	}
   151  	var r *types.Receipt
   152  	err := rb.rpcClient.CallContext(ctx, &r, "klay_getTransactionReceipt", txHash)
   153  	if err == nil && r == nil {
   154  		return nil, klaytn.NotFound
   155  	}
   156  	return r, err
   157  }
   158  
   159  func (rb *RemoteBackend) TransactionReceiptRpcOutput(ctx context.Context, txHash common.Hash) (r map[string]interface{}, err error) {
   160  	if !rb.checkParentPeer() {
   161  		return nil, NoParentPeerErr
   162  	}
   163  
   164  	err = rb.rpcClient.CallContext(ctx, &r, "klay_getTransactionReceipt", txHash)
   165  	if err == nil && r == nil {
   166  		return nil, klaytn.NotFound
   167  	}
   168  	return
   169  }
   170  
   171  // ChainID returns the chain ID of the sub-bridge configuration.
   172  func (rb *RemoteBackend) ChainID(ctx context.Context) (*big.Int, error) {
   173  	return big.NewInt(int64(rb.subBridge.config.ParentChainID)), nil
   174  }
   175  
   176  func (rb *RemoteBackend) FilterLogs(ctx context.Context, query klaytn.FilterQuery) (result []types.Log, err error) {
   177  	if !rb.checkParentPeer() {
   178  		return nil, NoParentPeerErr
   179  	}
   180  	err = rb.rpcClient.CallContext(ctx, &result, "klay_getLogs", toFilterArg(query))
   181  	return
   182  }
   183  
   184  func (rb *RemoteBackend) SubscribeFilterLogs(ctx context.Context, query klaytn.FilterQuery, ch chan<- types.Log) (klaytn.Subscription, error) {
   185  	if !rb.checkParentPeer() {
   186  		return nil, NoParentPeerErr
   187  	}
   188  	return rb.rpcClient.KlaySubscribe(ctx, ch, "logs", toFilterArg(query))
   189  }
   190  
   191  // CurrentBlockNumber returns a current block number.
   192  func (rb *RemoteBackend) CurrentBlockNumber(ctx context.Context) (uint64, error) {
   193  	if !rb.checkParentPeer() {
   194  		return 0, NoParentPeerErr
   195  	}
   196  	var result hexutil.Uint64
   197  	err := rb.rpcClient.CallContext(ctx, &result, "klay_blockNumber")
   198  	return uint64(result), err
   199  }
   200  
   201  func toFilterArg(q klaytn.FilterQuery) interface{} {
   202  	arg := map[string]interface{}{
   203  		"fromBlock": toBlockNumArg(q.FromBlock),
   204  		"toBlock":   toBlockNumArg(q.ToBlock),
   205  		"address":   q.Addresses,
   206  		"topics":    q.Topics,
   207  	}
   208  	if q.FromBlock == nil {
   209  		arg["fromBlock"] = "0x0"
   210  	}
   211  	return arg
   212  }
   213  
   214  func toBlockNumArg(number *big.Int) string {
   215  	if number == nil {
   216  		return "latest"
   217  	}
   218  	return hexutil.EncodeBig(number)
   219  }
   220  
   221  func toCallArg(msg klaytn.CallMsg) interface{} {
   222  	arg := map[string]interface{}{
   223  		"from": msg.From,
   224  		"to":   msg.To,
   225  	}
   226  	if len(msg.Data) > 0 {
   227  		arg["data"] = hexutil.Bytes(msg.Data)
   228  	}
   229  	if msg.Value != nil {
   230  		arg["value"] = (*hexutil.Big)(msg.Value)
   231  	}
   232  	if msg.Gas != 0 {
   233  		arg["gas"] = hexutil.Uint64(msg.Gas)
   234  	}
   235  	if msg.GasPrice != nil {
   236  		arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
   237  	}
   238  	return arg
   239  }