github.com/vantum/vantum@v0.0.0-20180815184342-fe37d5f7a990/accounts/abi/bind/backends/simulated.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package backends 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "math/big" 24 "sync" 25 "time" 26 27 "github.com/vantum/vantum" 28 "github.com/vantum/vantum/accounts/abi/bind" 29 "github.com/vantum/vantum/common" 30 "github.com/vantum/vantum/common/math" 31 "github.com/vantum/vantum/consensus/ethash" 32 "github.com/vantum/vantum/core" 33 "github.com/vantum/vantum/core/bloombits" 34 "github.com/vantum/vantum/core/state" 35 "github.com/vantum/vantum/core/types" 36 "github.com/vantum/vantum/core/vm" 37 "github.com/vantum/vantum/eth/filters" 38 "github.com/vantum/vantum/ethdb" 39 "github.com/vantum/vantum/event" 40 "github.com/vantum/vantum/params" 41 "github.com/vantum/vantum/rpc" 42 ) 43 44 // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. 45 var _ bind.ContractBackend = (*SimulatedBackend)(nil) 46 47 var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block") 48 var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction") 49 50 // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in 51 // the background. Its main purpose is to allow easily testing contract bindings. 52 type SimulatedBackend struct { 53 database ethdb.Database // In memory database to store our testing data 54 blockchain *core.BlockChain // Ethereum blockchain to handle the consensus 55 56 mu sync.Mutex 57 pendingBlock *types.Block // Currently pending block that will be imported on request 58 pendingState *state.StateDB // Currently pending state that will be the active on on request 59 60 events *filters.EventSystem // Event system for filtering log events live 61 62 config *params.ChainConfig 63 } 64 65 // NewSimulatedBackend creates a new binding backend using a simulated blockchain 66 // for testing purposes. 67 func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend { 68 database, _ := ethdb.NewMemDatabase() 69 genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc} 70 genesis.MustCommit(database) 71 blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}) 72 73 backend := &SimulatedBackend{ 74 database: database, 75 blockchain: blockchain, 76 config: genesis.Config, 77 events: filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false), 78 } 79 backend.rollback() 80 return backend 81 } 82 83 // Commit imports all the pending transactions as a single block and starts a 84 // fresh new state. 85 func (b *SimulatedBackend) Commit() { 86 b.mu.Lock() 87 defer b.mu.Unlock() 88 89 if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil { 90 panic(err) // This cannot happen unless the simulator is wrong, fail in that case 91 } 92 b.rollback() 93 } 94 95 // Rollback aborts all pending transactions, reverting to the last committed state. 96 func (b *SimulatedBackend) Rollback() { 97 b.mu.Lock() 98 defer b.mu.Unlock() 99 100 b.rollback() 101 } 102 103 func (b *SimulatedBackend) rollback() { 104 blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {}) 105 statedb, _ := b.blockchain.State() 106 107 b.pendingBlock = blocks[0] 108 b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database()) 109 } 110 111 // CodeAt returns the code associated with a certain account in the blockchain. 112 func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { 113 b.mu.Lock() 114 defer b.mu.Unlock() 115 116 if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { 117 return nil, errBlockNumberUnsupported 118 } 119 statedb, _ := b.blockchain.State() 120 return statedb.GetCode(contract), nil 121 } 122 123 // BalanceAt returns the wei balance of a certain account in the blockchain. 124 func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) { 125 b.mu.Lock() 126 defer b.mu.Unlock() 127 128 if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { 129 return nil, errBlockNumberUnsupported 130 } 131 statedb, _ := b.blockchain.State() 132 return statedb.GetBalance(contract), nil 133 } 134 135 // NonceAt returns the nonce of a certain account in the blockchain. 136 func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) { 137 b.mu.Lock() 138 defer b.mu.Unlock() 139 140 if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { 141 return 0, errBlockNumberUnsupported 142 } 143 statedb, _ := b.blockchain.State() 144 return statedb.GetNonce(contract), nil 145 } 146 147 // StorageAt returns the value of key in the storage of an account in the blockchain. 148 func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { 149 b.mu.Lock() 150 defer b.mu.Unlock() 151 152 if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { 153 return nil, errBlockNumberUnsupported 154 } 155 statedb, _ := b.blockchain.State() 156 val := statedb.GetState(contract, key) 157 return val[:], nil 158 } 159 160 // TransactionReceipt returns the receipt of a transaction. 161 func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { 162 receipt, _, _, _ := core.GetReceipt(b.database, txHash) 163 return receipt, nil 164 } 165 166 // PendingCodeAt returns the code associated with an account in the pending state. 167 func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { 168 b.mu.Lock() 169 defer b.mu.Unlock() 170 171 return b.pendingState.GetCode(contract), nil 172 } 173 174 // CallContract executes a contract call. 175 func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { 176 b.mu.Lock() 177 defer b.mu.Unlock() 178 179 if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { 180 return nil, errBlockNumberUnsupported 181 } 182 state, err := b.blockchain.State() 183 if err != nil { 184 return nil, err 185 } 186 rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) 187 return rval, err 188 } 189 190 // PendingCallContract executes a contract call on the pending state. 191 func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { 192 b.mu.Lock() 193 defer b.mu.Unlock() 194 defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) 195 196 rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) 197 return rval, err 198 } 199 200 // PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving 201 // the nonce currently pending for the account. 202 func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 203 b.mu.Lock() 204 defer b.mu.Unlock() 205 206 return b.pendingState.GetOrNewStateObject(account).Nonce(), nil 207 } 208 209 // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated 210 // chain doens't have miners, we just return a gas price of 1 for any call. 211 func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 212 return big.NewInt(1), nil 213 } 214 215 // EstimateGas executes the requested code against the currently pending block/state and 216 // returns the used amount of gas. 217 func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { 218 b.mu.Lock() 219 defer b.mu.Unlock() 220 221 // Determine the lowest and highest possible gas limits to binary search in between 222 var ( 223 lo uint64 = params.TxGas - 1 224 hi uint64 225 cap uint64 226 ) 227 if call.Gas >= params.TxGas { 228 hi = call.Gas 229 } else { 230 hi = b.pendingBlock.GasLimit() 231 } 232 cap = hi 233 234 // Create a helper to check if a gas allowance results in an executable transaction 235 executable := func(gas uint64) bool { 236 call.Gas = gas 237 238 snapshot := b.pendingState.Snapshot() 239 _, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) 240 b.pendingState.RevertToSnapshot(snapshot) 241 242 if err != nil || failed { 243 return false 244 } 245 return true 246 } 247 // Execute the binary search and hone in on an executable gas limit 248 for lo+1 < hi { 249 mid := (hi + lo) / 2 250 if !executable(mid) { 251 lo = mid 252 } else { 253 hi = mid 254 } 255 } 256 // Reject the transaction as invalid if it still fails at the highest allowance 257 if hi == cap { 258 if !executable(hi) { 259 return 0, errGasEstimationFailed 260 } 261 } 262 return hi, nil 263 } 264 265 // callContract implements common code between normal and pending contract calls. 266 // state is modified during execution, make sure to copy it if necessary. 267 func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) { 268 // Ensure message is initialized properly. 269 if call.GasPrice == nil { 270 call.GasPrice = big.NewInt(1) 271 } 272 if call.Gas == 0 { 273 call.Gas = 50000000 274 } 275 if call.Value == nil { 276 call.Value = new(big.Int) 277 } 278 // Set infinite balance to the fake caller account. 279 from := statedb.GetOrNewStateObject(call.From) 280 from.SetBalance(math.MaxBig256) 281 // Execute the call. 282 msg := callmsg{call} 283 284 evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil) 285 // Create a new environment which holds all relevant information 286 // about the transaction and calling mechanisms. 287 vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{}) 288 gaspool := new(core.GasPool).AddGas(math.MaxUint64) 289 290 return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() 291 } 292 293 // SendTransaction updates the pending block to include the given transaction. 294 // It panics if the transaction is invalid. 295 func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { 296 b.mu.Lock() 297 defer b.mu.Unlock() 298 299 sender, err := types.Sender(types.HomesteadSigner{}, tx) 300 if err != nil { 301 panic(fmt.Errorf("invalid transaction: %v", err)) 302 } 303 nonce := b.pendingState.GetNonce(sender) 304 if tx.Nonce() != nonce { 305 panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) 306 } 307 308 blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { 309 for _, tx := range b.pendingBlock.Transactions() { 310 block.AddTx(tx) 311 } 312 block.AddTx(tx) 313 }) 314 statedb, _ := b.blockchain.State() 315 316 b.pendingBlock = blocks[0] 317 b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database()) 318 return nil 319 } 320 321 // FilterLogs executes a log filter operation, blocking during execution and 322 // returning all the results in one batch. 323 // 324 // TODO(karalabe): Deprecate when the subscription one can return past data too. 325 func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { 326 // Initialize unset filter boundaried to run from genesis to chain head 327 from := int64(0) 328 if query.FromBlock != nil { 329 from = query.FromBlock.Int64() 330 } 331 to := int64(-1) 332 if query.ToBlock != nil { 333 to = query.ToBlock.Int64() 334 } 335 // Construct and execute the filter 336 filter := filters.New(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics) 337 338 logs, err := filter.Logs(ctx) 339 if err != nil { 340 return nil, err 341 } 342 res := make([]types.Log, len(logs)) 343 for i, log := range logs { 344 res[i] = *log 345 } 346 return res, nil 347 } 348 349 // SubscribeFilterLogs creates a background log filtering operation, returning a 350 // subscription immediately, which can be used to stream the found events. 351 func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { 352 // Subscribe to contract events 353 sink := make(chan []*types.Log) 354 355 sub, err := b.events.SubscribeLogs(query, sink) 356 if err != nil { 357 return nil, err 358 } 359 // Since we're getting logs in batches, we need to flatten them into a plain stream 360 return event.NewSubscription(func(quit <-chan struct{}) error { 361 defer sub.Unsubscribe() 362 for { 363 select { 364 case logs := <-sink: 365 for _, log := range logs { 366 select { 367 case ch <- *log: 368 case err := <-sub.Err(): 369 return err 370 case <-quit: 371 return nil 372 } 373 } 374 case err := <-sub.Err(): 375 return err 376 case <-quit: 377 return nil 378 } 379 } 380 }), nil 381 } 382 383 // AdjustTime adds a time shift to the simulated clock. 384 func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error { 385 b.mu.Lock() 386 defer b.mu.Unlock() 387 blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) { 388 for _, tx := range b.pendingBlock.Transactions() { 389 block.AddTx(tx) 390 } 391 block.OffsetTime(int64(adjustment.Seconds())) 392 }) 393 statedb, _ := b.blockchain.State() 394 395 b.pendingBlock = blocks[0] 396 b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database()) 397 398 return nil 399 } 400 401 // callmsg implements core.Message to allow passing it as a transaction simulator. 402 type callmsg struct { 403 ethereum.CallMsg 404 } 405 406 func (m callmsg) From() common.Address { return m.CallMsg.From } 407 func (m callmsg) Nonce() uint64 { return 0 } 408 func (m callmsg) CheckNonce() bool { return false } 409 func (m callmsg) To() *common.Address { return m.CallMsg.To } 410 func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } 411 func (m callmsg) Gas() uint64 { return m.CallMsg.Gas } 412 func (m callmsg) Value() *big.Int { return m.CallMsg.Value } 413 func (m callmsg) Data() []byte { return m.CallMsg.Data } 414 415 // filterBackend implements filters.Backend to support filtering for logs without 416 // taking bloom-bits acceleration structures into account. 417 type filterBackend struct { 418 db ethdb.Database 419 bc *core.BlockChain 420 } 421 422 func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db } 423 func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") } 424 425 func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) { 426 if block == rpc.LatestBlockNumber { 427 return fb.bc.CurrentHeader(), nil 428 } 429 return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil 430 } 431 func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { 432 return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil 433 } 434 435 func (fb *filterBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription { 436 return event.NewSubscription(func(quit <-chan struct{}) error { 437 <-quit 438 return nil 439 }) 440 } 441 func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { 442 return fb.bc.SubscribeChainEvent(ch) 443 } 444 func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { 445 return fb.bc.SubscribeRemovedLogsEvent(ch) 446 } 447 func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { 448 return fb.bc.SubscribeLogsEvent(ch) 449 } 450 451 func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 } 452 func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) { 453 panic("not supported") 454 }