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 }