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 }