github.com/klaytn/klaytn@v1.12.1/accounts/abi/bind/backends/blockchain.go (about) 1 // Copyright 2023 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 backends 18 19 import ( 20 "context" 21 "errors" 22 "math/big" 23 24 "github.com/klaytn/klaytn" 25 "github.com/klaytn/klaytn/accounts/abi/bind" 26 "github.com/klaytn/klaytn/blockchain" 27 "github.com/klaytn/klaytn/blockchain/state" 28 "github.com/klaytn/klaytn/blockchain/types" 29 "github.com/klaytn/klaytn/blockchain/vm" 30 "github.com/klaytn/klaytn/common" 31 "github.com/klaytn/klaytn/event" 32 "github.com/klaytn/klaytn/node/cn/filters" 33 "github.com/klaytn/klaytn/params" 34 ) 35 36 // Maintain separate minimal interfaces of blockchain.BlockChain because ContractBackend are used 37 // in various situations. BlockChain instances are often passed down as different interfaces such as 38 // consensus.ChainReader, governance.blockChain, work.BlockChain. 39 type BlockChainForCaller interface { 40 // Required by NewEVMContext 41 blockchain.ChainContext 42 43 // Below is a subset of consensus.ChainReader 44 // Only using the vocabulary of consensus.ChainReader for potential 45 // usability within consensus package. 46 Config() *params.ChainConfig 47 GetHeaderByNumber(number uint64) *types.Header 48 GetBlock(hash common.Hash, number uint64) *types.Block 49 State() (*state.StateDB, error) 50 StateAt(root common.Hash) (*state.StateDB, error) 51 CurrentBlock() *types.Block 52 } 53 54 // Maintain separate minimal interfaces of blockchain.TxPool because ContractBackend are used 55 // in various situations. TxPool instances are often passed down as work.TxPool. 56 type TxPoolForCaller interface { 57 // Below is a subset of work.TxPool 58 GetPendingNonce(addr common.Address) uint64 59 AddLocal(tx *types.Transaction) error 60 GasPrice() *big.Int 61 } 62 63 // BlockchainContractBackend implements bind.Contract* and bind.DeployBackend, based on 64 // a user-supplied blockchain.BlockChain instance. 65 // Its intended purpose is reading system contracts during block processing. 66 // 67 // Note that SimulatedBackend creates a new temporary BlockChain for testing, 68 // whereas BlockchainContractBackend uses an existing BlockChain with existing database. 69 type BlockchainContractBackend struct { 70 bc BlockChainForCaller 71 txPool TxPoolForCaller 72 events *filters.EventSystem 73 } 74 75 // This nil assignment ensures at compile time that BlockchainContractBackend implements bind.Contract* and bind.DeployBackend. 76 var ( 77 _ bind.ContractCaller = (*BlockchainContractBackend)(nil) 78 _ bind.ContractTransactor = (*BlockchainContractBackend)(nil) 79 _ bind.ContractFilterer = (*BlockchainContractBackend)(nil) 80 _ bind.DeployBackend = (*BlockchainContractBackend)(nil) 81 _ bind.ContractBackend = (*BlockchainContractBackend)(nil) 82 ) 83 84 // `txPool` is required for bind.ContractTransactor methods and `events` is required for bind.ContractFilterer methods. 85 // If `tp=nil`, bind.ContractTransactor methods could return errors. 86 // If `es=nil`, bind.ContractFilterer methods could return errors. 87 func NewBlockchainContractBackend(bc BlockChainForCaller, tp TxPoolForCaller, es *filters.EventSystem) *BlockchainContractBackend { 88 return &BlockchainContractBackend{bc, tp, es} 89 } 90 91 // bind.ContractCaller defined methods 92 93 func (b *BlockchainContractBackend) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { 94 if _, state, err := b.getBlockAndState(blockNumber); err != nil { 95 return nil, err 96 } else { 97 return state.GetCode(account), nil 98 } 99 } 100 101 // Executes a read-only function call with respect to the specified block's state, or latest state if not specified. 102 // 103 // Returns call result in []byte. 104 // Returns error when: 105 // - cannot find the corresponding block or stateDB 106 // - VM revert error 107 // - VM other errors (e.g. NotProgramAccount, OutOfGas) 108 // - Error outside VM 109 func (b *BlockchainContractBackend) CallContract(ctx context.Context, call klaytn.CallMsg, blockNumber *big.Int) ([]byte, error) { 110 block, state, err := b.getBlockAndState(blockNumber) 111 if err != nil { 112 return nil, err 113 } 114 115 res, err := b.callContract(call, block, state) 116 if err != nil { 117 return nil, err 118 } 119 if len(res.Revert()) > 0 { 120 return nil, blockchain.NewRevertError(res) 121 } 122 return res.Return(), res.Unwrap() 123 } 124 125 func (b *BlockchainContractBackend) callContract(call klaytn.CallMsg, block *types.Block, state *state.StateDB) (*blockchain.ExecutionResult, error) { 126 if call.Gas == 0 { 127 call.Gas = uint64(3e8) // enough gas for ordinary contract calls 128 } 129 130 intrinsicGas, err := types.IntrinsicGas(call.Data, nil, call.To == nil, b.bc.Config().Rules(block.Number())) 131 if err != nil { 132 return nil, err 133 } 134 135 var accessList types.AccessList 136 if call.AccessList != nil { 137 accessList = *call.AccessList 138 } 139 msg := types.NewMessage(call.From, call.To, 0, call.Value, call.Gas, call.GasPrice, call.Data, 140 false, intrinsicGas, accessList) 141 142 txContext := blockchain.NewEVMTxContext(msg, block.Header()) 143 blockContext := blockchain.NewEVMBlockContext(block.Header(), b.bc, nil) 144 145 // EVM demands the sender to have enough KLAY balance (gasPrice * gasLimit) in buyGas() 146 // After KIP-71, gasPrice is nonzero baseFee, regardless of the msg.gasPrice (usually 0) 147 // But our sender (usually 0x0) won't have enough balance. Instead we override gasPrice = 0 here 148 txContext.GasPrice = big.NewInt(0) 149 evm := vm.NewEVM(blockContext, txContext, state, b.bc.Config(), &vm.Config{}) 150 151 return blockchain.ApplyMessage(evm, msg) 152 } 153 154 func (b *BlockchainContractBackend) getBlockAndState(num *big.Int) (*types.Block, *state.StateDB, error) { 155 var block *types.Block 156 if num == nil { 157 block = b.bc.CurrentBlock() 158 } else { 159 header := b.bc.GetHeaderByNumber(num.Uint64()) 160 if header == nil { 161 return nil, nil, errBlockDoesNotExist 162 } 163 block = b.bc.GetBlock(header.Hash(), header.Number.Uint64()) 164 } 165 if block == nil { 166 return nil, nil, errBlockDoesNotExist 167 } 168 169 state, err := b.bc.StateAt(block.Root()) 170 return block, state, err 171 } 172 173 // bind.ContractTransactor defined methods 174 175 func (b *BlockchainContractBackend) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 176 // TODO-Klaytn this is not pending code but latest code 177 state, err := b.bc.State() 178 if err != nil { 179 return nil, err 180 } 181 return state.GetCode(account), nil 182 } 183 184 func (b *BlockchainContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 185 if b.txPool != nil { 186 return b.txPool.GetPendingNonce(account), nil 187 } 188 // TODO-Klaytn this is not pending nonce but latest nonce 189 state, err := b.bc.State() 190 if err != nil { 191 return 0, err 192 } 193 return state.GetNonce(account), nil 194 } 195 196 func (b *BlockchainContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 197 if b.bc.Config().IsMagmaForkEnabled(b.bc.CurrentBlock().Number()) { 198 if b.txPool != nil { 199 return new(big.Int).Mul(b.txPool.GasPrice(), big.NewInt(2)), nil 200 } else { 201 return new(big.Int).Mul(b.bc.CurrentBlock().Header().BaseFee, big.NewInt(2)), nil 202 } 203 } else { 204 return new(big.Int).SetUint64(b.bc.Config().UnitPrice), nil 205 } 206 } 207 208 func (b *BlockchainContractBackend) EstimateGas(ctx context.Context, call klaytn.CallMsg) (uint64, error) { 209 state, err := b.bc.State() 210 if err != nil { 211 return 0, err 212 } 213 balance := state.GetBalance(call.From) // from can't be nil 214 215 // Create a helper to check if a gas allowance results in an executable transaction 216 executable := func(gas uint64) (bool, *blockchain.ExecutionResult, error) { 217 call.Gas = gas 218 219 currentState, err := b.bc.State() 220 if err != nil { 221 return true, nil, nil 222 } 223 res, err := b.callContract(call, b.bc.CurrentBlock(), currentState) 224 if err != nil { 225 if errors.Is(err, blockchain.ErrIntrinsicGas) { 226 return true, nil, nil // Special case, raise gas limit 227 } 228 return true, nil, err // Bail out 229 } 230 return res.Failed(), res, nil 231 } 232 233 estimated, err := blockchain.DoEstimateGas(ctx, call.Gas, 0, call.Value, call.GasPrice, balance, executable) 234 return uint64(estimated), err 235 } 236 237 func (b *BlockchainContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { 238 if b.txPool == nil { 239 return errors.New("tx pool not configured") 240 } 241 return b.txPool.AddLocal(tx) 242 } 243 244 func (b *BlockchainContractBackend) ChainID(ctx context.Context) (*big.Int, error) { 245 return b.bc.Config().ChainID, nil 246 } 247 248 // bind.ContractFilterer defined methods 249 250 func (b *BlockchainContractBackend) FilterLogs(ctx context.Context, query klaytn.FilterQuery) ([]types.Log, error) { 251 // Convert the current block numbers into internal representations 252 if query.FromBlock == nil { 253 query.FromBlock = big.NewInt(b.bc.CurrentBlock().Number().Int64()) 254 } 255 if query.ToBlock == nil { 256 query.ToBlock = big.NewInt(b.bc.CurrentBlock().Number().Int64()) 257 } 258 from := query.FromBlock.Int64() 259 to := query.ToBlock.Int64() 260 261 state, err := b.bc.State() 262 if err != nil { 263 return nil, err 264 } 265 bc, ok := b.bc.(*blockchain.BlockChain) 266 if !ok { 267 return nil, errors.New("BlockChainForCaller is not blockchain.BlockChain") 268 } 269 filter := filters.NewRangeFilter(&filterBackend{state.Database().TrieDB().DiskDB(), bc}, from, to, query.Addresses, query.Topics) 270 271 logs, err := filter.Logs(ctx) 272 if err != nil { 273 return nil, err 274 } 275 res := make([]types.Log, len(logs)) 276 for i, log := range logs { 277 res[i] = *log 278 } 279 return res, nil 280 } 281 282 func (b *BlockchainContractBackend) SubscribeFilterLogs(ctx context.Context, query klaytn.FilterQuery, ch chan<- types.Log) (klaytn.Subscription, error) { 283 // Subscribe to contract events 284 sink := make(chan []*types.Log) 285 286 if b.events == nil { 287 return nil, errors.New("events system not configured") 288 } 289 sub, err := b.events.SubscribeLogs(query, sink) 290 if err != nil { 291 return nil, err 292 } 293 // Since we're getting logs in batches, we need to flatten them into a plain stream 294 return event.NewSubscription(func(quit <-chan struct{}) error { 295 defer sub.Unsubscribe() 296 for { 297 select { 298 case logs := <-sink: 299 for _, log := range logs { 300 select { 301 case ch <- *log: 302 case err := <-sub.Err(): 303 return err 304 case <-quit: 305 return nil 306 } 307 } 308 case err := <-sub.Err(): 309 return err 310 case <-quit: 311 return nil 312 } 313 } 314 }), nil 315 } 316 317 // bind.DeployBackend defined methods 318 319 func (b *BlockchainContractBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { 320 bc, ok := b.bc.(*blockchain.BlockChain) 321 if !ok { 322 return nil, errors.New("BlockChainForCaller is not blockchain.BlockChain") 323 } 324 receipt := bc.GetReceiptByTxHash(txHash) 325 if receipt != nil { 326 return receipt, nil 327 } 328 return nil, errors.New("receipt does not exist") 329 } 330 331 // sc.Backend requires BalanceAt and CurrentBlockNumber 332 333 func (b *BlockchainContractBackend) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { 334 if _, state, err := b.getBlockAndState(blockNumber); err != nil { 335 return nil, err 336 } else { 337 return state.GetBalance(account), nil 338 } 339 } 340 341 func (b *BlockchainContractBackend) CurrentBlockNumber(ctx context.Context) (uint64, error) { 342 return b.bc.CurrentBlock().NumberU64(), nil 343 }