github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/accounts/abi/bind/backends/simulated.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:30</date>
    10  //</624450059852845056>
    11  
    12  
    13  package backends
    14  
    15  import (
    16  	"context"
    17  	"errors"
    18  	"fmt"
    19  	"math/big"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/ethereum/go-ethereum"
    24  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/common/math"
    27  	"github.com/ethereum/go-ethereum/consensus/ethash"
    28  	"github.com/ethereum/go-ethereum/core"
    29  	"github.com/ethereum/go-ethereum/core/bloombits"
    30  	"github.com/ethereum/go-ethereum/core/rawdb"
    31  	"github.com/ethereum/go-ethereum/core/state"
    32  	"github.com/ethereum/go-ethereum/core/types"
    33  	"github.com/ethereum/go-ethereum/core/vm"
    34  	"github.com/ethereum/go-ethereum/eth/filters"
    35  	"github.com/ethereum/go-ethereum/ethdb"
    36  	"github.com/ethereum/go-ethereum/event"
    37  	"github.com/ethereum/go-ethereum/params"
    38  	"github.com/ethereum/go-ethereum/rpc"
    39  )
    40  
    41  //这个nil分配确保了模拟后端实现bind.contractbackend的编译时间。
    42  var _ bind.ContractBackend = (*SimulatedBackend)(nil)
    43  
    44  var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
    45  var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
    46  
    47  //Simulatedbackend实现bind.contractbackend,在
    48  //背景。其主要目的是允许轻松测试合同绑定。
    49  type SimulatedBackend struct {
    50  database   ethdb.Database   //存储测试数据的内存数据库
    51  blockchain *core.BlockChain //以太坊区块链处理共识
    52  
    53  	mu           sync.Mutex
    54  pendingBlock *types.Block   //将根据请求导入的当前挂起块
    55  pendingState *state.StateDB //当前处于挂起状态,根据请求将处于活动状态
    56  
    57  events *filters.EventSystem //用于实时筛选日志事件的事件系统
    58  
    59  	config *params.ChainConfig
    60  }
    61  
    62  //NewSimulatedBackend使用模拟区块链创建新的绑定后端
    63  //用于测试。
    64  func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
    65  	database := ethdb.NewMemDatabase()
    66  	genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
    67  	genesis.MustCommit(database)
    68  	blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
    69  
    70  	backend := &SimulatedBackend{
    71  		database:   database,
    72  		blockchain: blockchain,
    73  		config:     genesis.Config,
    74  		events:     filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false),
    75  	}
    76  	backend.rollback()
    77  	return backend
    78  }
    79  
    80  //commit将所有挂起的事务作为单个块导入并启动
    81  //新的状态。
    82  func (b *SimulatedBackend) Commit() {
    83  	b.mu.Lock()
    84  	defer b.mu.Unlock()
    85  
    86  	if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
    87  panic(err) //除非模拟器出错,否则不会发生这种情况,在这种情况下会失败。
    88  	}
    89  	b.rollback()
    90  }
    91  
    92  //回滚将中止所有挂起的事务,恢复到上一次提交的状态。
    93  func (b *SimulatedBackend) Rollback() {
    94  	b.mu.Lock()
    95  	defer b.mu.Unlock()
    96  
    97  	b.rollback()
    98  }
    99  
   100  func (b *SimulatedBackend) rollback() {
   101  	blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
   102  	statedb, _ := b.blockchain.State()
   103  
   104  	b.pendingBlock = blocks[0]
   105  	b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
   106  }
   107  
   108  //codeat返回与区块链中某个帐户关联的代码。
   109  func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
   110  	b.mu.Lock()
   111  	defer b.mu.Unlock()
   112  
   113  	if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
   114  		return nil, errBlockNumberUnsupported
   115  	}
   116  	statedb, _ := b.blockchain.State()
   117  	return statedb.GetCode(contract), nil
   118  }
   119  
   120  //balanceat返回区块链中某个账户的wei余额。
   121  func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
   122  	b.mu.Lock()
   123  	defer b.mu.Unlock()
   124  
   125  	if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
   126  		return nil, errBlockNumberUnsupported
   127  	}
   128  	statedb, _ := b.blockchain.State()
   129  	return statedb.GetBalance(contract), nil
   130  }
   131  
   132  //nonceat返回区块链中某个帐户的nonce。
   133  func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) {
   134  	b.mu.Lock()
   135  	defer b.mu.Unlock()
   136  
   137  	if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
   138  		return 0, errBlockNumberUnsupported
   139  	}
   140  	statedb, _ := b.blockchain.State()
   141  	return statedb.GetNonce(contract), nil
   142  }
   143  
   144  //storageat返回在区块链中存储帐户的密钥的值。
   145  func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
   146  	b.mu.Lock()
   147  	defer b.mu.Unlock()
   148  
   149  	if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
   150  		return nil, errBlockNumberUnsupported
   151  	}
   152  	statedb, _ := b.blockchain.State()
   153  	val := statedb.GetState(contract, key)
   154  	return val[:], nil
   155  }
   156  
   157  //TransactionReceipt返回交易的收据。
   158  func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
   159  	receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash)
   160  	return receipt, nil
   161  }
   162  
   163  //PendingCodeAt返回与处于挂起状态的帐户关联的代码。
   164  func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
   165  	b.mu.Lock()
   166  	defer b.mu.Unlock()
   167  
   168  	return b.pendingState.GetCode(contract), nil
   169  }
   170  
   171  //CallContract执行合同调用。
   172  func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
   173  	b.mu.Lock()
   174  	defer b.mu.Unlock()
   175  
   176  	if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
   177  		return nil, errBlockNumberUnsupported
   178  	}
   179  	state, err := b.blockchain.State()
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
   184  	return rval, err
   185  }
   186  
   187  //PendingCallContract对挂起状态执行合同调用。
   188  func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
   189  	b.mu.Lock()
   190  	defer b.mu.Unlock()
   191  	defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
   192  
   193  	rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
   194  	return rval, err
   195  }
   196  
   197  //PendingOnCate实现PendingStateReader.PendingOnCate,检索
   198  //当前为帐户挂起的非现金。
   199  func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
   200  	b.mu.Lock()
   201  	defer b.mu.Unlock()
   202  
   203  	return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
   204  }
   205  
   206  //Suggestgasprice执行ContractTransactor.Suggestgasprice。自从模拟
   207  //这家连锁店没有矿工,我们只要回电1美元就可以了。
   208  func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
   209  	return big.NewInt(1), nil
   210  }
   211  
   212  //EstimateGas针对当前挂起的块/状态执行请求的代码,并且
   213  //返回使用的气体量。
   214  func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) {
   215  	b.mu.Lock()
   216  	defer b.mu.Unlock()
   217  
   218  //确定二元搜索的最低和最高气体限制
   219  	var (
   220  		lo  uint64 = params.TxGas - 1
   221  		hi  uint64
   222  		cap uint64
   223  	)
   224  	if call.Gas >= params.TxGas {
   225  		hi = call.Gas
   226  	} else {
   227  		hi = b.pendingBlock.GasLimit()
   228  	}
   229  	cap = hi
   230  
   231  //创建一个助手以检查气体限额是否导致可执行事务
   232  	executable := func(gas uint64) bool {
   233  		call.Gas = gas
   234  
   235  		snapshot := b.pendingState.Snapshot()
   236  		_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
   237  		b.pendingState.RevertToSnapshot(snapshot)
   238  
   239  		if err != nil || failed {
   240  			return false
   241  		}
   242  		return true
   243  	}
   244  //执行二进制搜索并按可执行的气体限值接通。
   245  	for lo+1 < hi {
   246  		mid := (hi + lo) / 2
   247  		if !executable(mid) {
   248  			lo = mid
   249  		} else {
   250  			hi = mid
   251  		}
   252  	}
   253  //如果交易仍以最高限额失败,则将其视为无效拒绝交易
   254  	if hi == cap {
   255  		if !executable(hi) {
   256  			return 0, errGasEstimationFailed
   257  		}
   258  	}
   259  	return hi, nil
   260  }
   261  
   262  //CallContract实现正常和挂起的合同调用之间的公共代码。
   263  //状态在执行期间被修改,请确保在必要时复制它。
   264  func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
   265  //确保消息已正确初始化。
   266  	if call.GasPrice == nil {
   267  		call.GasPrice = big.NewInt(1)
   268  	}
   269  	if call.Gas == 0 {
   270  		call.Gas = 50000000
   271  	}
   272  	if call.Value == nil {
   273  		call.Value = new(big.Int)
   274  	}
   275  //将无限余额设置为假呼叫者帐户。
   276  	from := statedb.GetOrNewStateObject(call.From)
   277  	from.SetBalance(math.MaxBig256)
   278  //执行呼叫。
   279  	msg := callmsg{call}
   280  
   281  	evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
   282  //创建一个保存所有相关信息的新环境
   283  //关于事务和调用机制。
   284  	vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
   285  	gaspool := new(core.GasPool).AddGas(math.MaxUint64)
   286  
   287  	return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
   288  }
   289  
   290  //sendTransaction更新挂起块以包括给定的事务。
   291  //如果事务无效,它会恐慌。
   292  func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
   293  	b.mu.Lock()
   294  	defer b.mu.Unlock()
   295  
   296  	sender, err := types.Sender(types.HomesteadSigner{}, tx)
   297  	if err != nil {
   298  		panic(fmt.Errorf("invalid transaction: %v", err))
   299  	}
   300  	nonce := b.pendingState.GetNonce(sender)
   301  	if tx.Nonce() != nonce {
   302  		panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
   303  	}
   304  
   305  	blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
   306  		for _, tx := range b.pendingBlock.Transactions() {
   307  			block.AddTxWithChain(b.blockchain, tx)
   308  		}
   309  		block.AddTxWithChain(b.blockchain, tx)
   310  	})
   311  	statedb, _ := b.blockchain.State()
   312  
   313  	b.pendingBlock = blocks[0]
   314  	b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
   315  	return nil
   316  }
   317  
   318  //filterlogs执行日志筛选操作,在执行期间阻塞,以及
   319  //一批返回所有结果。
   320  //
   321  //TODO(karalabe):当订阅可以返回过去的数据时,取消预测。
   322  func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
   323  	var filter *filters.Filter
   324  	if query.BlockHash != nil {
   325  //请求块筛选器,构造一个单镜头筛选器
   326  		filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
   327  	} else {
   328  //初始化从Genesis运行到链头的未设置过滤器
   329  		from := int64(0)
   330  		if query.FromBlock != nil {
   331  			from = query.FromBlock.Int64()
   332  		}
   333  		to := int64(-1)
   334  		if query.ToBlock != nil {
   335  			to = query.ToBlock.Int64()
   336  		}
   337  //构造范围过滤器
   338  		filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
   339  	}
   340  //运行过滤器并返回所有日志
   341  	logs, err := filter.Logs(ctx)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	res := make([]types.Log, len(logs))
   346  	for i, log := range logs {
   347  		res[i] = *log
   348  	}
   349  	return res, nil
   350  }
   351  
   352  //subscribeBilterLogs创建后台日志筛选操作,返回
   353  //立即订阅,可用于流式处理找到的事件。
   354  func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
   355  //订阅合同事件
   356  	sink := make(chan []*types.Log)
   357  
   358  	sub, err := b.events.SubscribeLogs(query, sink)
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  //因为我们要批量获取日志,所以我们需要将它们展平成一条普通的流。
   363  	return event.NewSubscription(func(quit <-chan struct{}) error {
   364  		defer sub.Unsubscribe()
   365  		for {
   366  			select {
   367  			case logs := <-sink:
   368  				for _, log := range logs {
   369  					select {
   370  					case ch <- *log:
   371  					case err := <-sub.Err():
   372  						return err
   373  					case <-quit:
   374  						return nil
   375  					}
   376  				}
   377  			case err := <-sub.Err():
   378  				return err
   379  			case <-quit:
   380  				return nil
   381  			}
   382  		}
   383  	}), nil
   384  }
   385  
   386  //AdjustTime为模拟时钟增加了一个时间偏移。
   387  func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
   388  	b.mu.Lock()
   389  	defer b.mu.Unlock()
   390  	blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
   391  		for _, tx := range b.pendingBlock.Transactions() {
   392  			block.AddTx(tx)
   393  		}
   394  		block.OffsetTime(int64(adjustment.Seconds()))
   395  	})
   396  	statedb, _ := b.blockchain.State()
   397  
   398  	b.pendingBlock = blocks[0]
   399  	b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
   400  
   401  	return nil
   402  }
   403  
   404  //callmsg实现core.message以允许将其作为事务模拟器传递。
   405  type callmsg struct {
   406  	ethereum.CallMsg
   407  }
   408  
   409  func (m callmsg) From() common.Address { return m.CallMsg.From }
   410  func (m callmsg) Nonce() uint64        { return 0 }
   411  func (m callmsg) CheckNonce() bool     { return false }
   412  func (m callmsg) To() *common.Address  { return m.CallMsg.To }
   413  func (m callmsg) GasPrice() *big.Int   { return m.CallMsg.GasPrice }
   414  func (m callmsg) Gas() uint64          { return m.CallMsg.Gas }
   415  func (m callmsg) Value() *big.Int      { return m.CallMsg.Value }
   416  func (m callmsg) Data() []byte         { return m.CallMsg.Data }
   417  
   418  //filterbackend实现筛选器。backend支持筛选不包含
   419  //考虑到布卢姆钻头的加速结构。
   420  type filterBackend struct {
   421  	db ethdb.Database
   422  	bc *core.BlockChain
   423  }
   424  
   425  func (fb *filterBackend) ChainDb() ethdb.Database  { return fb.db }
   426  func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
   427  
   428  func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
   429  	if block == rpc.LatestBlockNumber {
   430  		return fb.bc.CurrentHeader(), nil
   431  	}
   432  	return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
   433  }
   434  
   435  func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
   436  	return fb.bc.GetHeaderByHash(hash), nil
   437  }
   438  
   439  func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
   440  	number := rawdb.ReadHeaderNumber(fb.db, hash)
   441  	if number == nil {
   442  		return nil, nil
   443  	}
   444  	return rawdb.ReadReceipts(fb.db, hash, *number), nil
   445  }
   446  
   447  func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
   448  	number := rawdb.ReadHeaderNumber(fb.db, hash)
   449  	if number == nil {
   450  		return nil, nil
   451  	}
   452  	receipts := rawdb.ReadReceipts(fb.db, hash, *number)
   453  	if receipts == nil {
   454  		return nil, nil
   455  	}
   456  	logs := make([][]*types.Log, len(receipts))
   457  	for i, receipt := range receipts {
   458  		logs[i] = receipt.Logs
   459  	}
   460  	return logs, nil
   461  }
   462  
   463  func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
   464  	return event.NewSubscription(func(quit <-chan struct{}) error {
   465  		<-quit
   466  		return nil
   467  	})
   468  }
   469  func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
   470  	return fb.bc.SubscribeChainEvent(ch)
   471  }
   472  func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
   473  	return fb.bc.SubscribeRemovedLogsEvent(ch)
   474  }
   475  func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
   476  	return fb.bc.SubscribeLogsEvent(ch)
   477  }
   478  
   479  func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
   480  func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
   481  	panic("not supported")
   482  }
   483