github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/accounts/abi/bind/base.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  //</624450060037394432>
    11  
    12  
    13  package bind
    14  
    15  import (
    16  	"context"
    17  	"errors"
    18  	"fmt"
    19  	"math/big"
    20  
    21  	"github.com/ethereum/go-ethereum"
    22  	"github.com/ethereum/go-ethereum/accounts/abi"
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/core/types"
    25  	"github.com/ethereum/go-ethereum/crypto"
    26  	"github.com/ethereum/go-ethereum/event"
    27  )
    28  
    29  //当约定要求方法
    30  //提交前签署交易。
    31  type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error)
    32  
    33  //Callopts是对合同调用请求进行微调的选项集合。
    34  type CallOpts struct {
    35  Pending     bool            //是否对挂起状态或最后一个已知状态进行操作
    36  From        common.Address  //可选发件人地址,否则使用第一个帐户
    37  BlockNumber *big.Int        //可选应在其上执行调用的块编号
    38  Context     context.Context //支持取消和超时的网络上下文(nil=无超时)
    39  }
    40  
    41  //TransactioOpts是创建
    42  //有效的以太坊事务。
    43  type TransactOpts struct {
    44  From   common.Address //用于发送交易的以太坊帐户
    45  Nonce  *big.Int       //nonce用于事务执行(nil=使用挂起状态)
    46  Signer SignerFn       //用于签署交易的方法(强制)
    47  
    48  Value    *big.Int //随交易转移的资金(零=0=无资金)
    49  GasPrice *big.Int //用于交易执行的天然气价格(零=天然气价格Oracle)
    50  GasLimit uint64   //为交易执行设定的气体限制(0=估计)
    51  
    52  Context context.Context //支持取消和超时的网络上下文(nil=无超时)
    53  }
    54  
    55  //filteropts是用于微调事件筛选的选项集合。
    56  //在有约束力的合同中。
    57  type FilterOpts struct {
    58  Start uint64  //查询范围的开始
    59  End   *uint64 //范围结束(零=最新)
    60  
    61  Context context.Context //支持取消和超时的网络上下文(nil=无超时)
    62  }
    63  
    64  //watchopts是对事件订阅进行微调的选项集合。
    65  //在有约束力的合同中。
    66  type WatchOpts struct {
    67  Start   *uint64         //查询范围的开始(nil=最新)
    68  Context context.Context //支持取消和超时的网络上下文(nil=无超时)
    69  }
    70  
    71  //BoundContract是反映在
    72  //以太坊网络。它包含由
    73  //要操作的更高级别合同绑定。
    74  type BoundContract struct {
    75  address    common.Address     //以太坊区块链上合同的部署地址
    76  abi        abi.ABI            //基于反射的ABI访问正确的以太坊方法
    77  caller     ContractCaller     //读取与区块链交互的界面
    78  transactor ContractTransactor //编写与区块链交互的接口
    79  filterer   ContractFilterer   //与区块链交互的事件过滤
    80  }
    81  
    82  //NewboundContract创建一个低级合同接口,通过它调用
    83  //交易可以通过。
    84  func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
    85  	return &BoundContract{
    86  		address:    address,
    87  		abi:        abi,
    88  		caller:     caller,
    89  		transactor: transactor,
    90  		filterer:   filterer,
    91  	}
    92  }
    93  
    94  //DeployContract将合同部署到以太坊区块链上,并绑定
    95  //使用Go包装器的部署地址。
    96  func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
    97  //否则,尝试部署合同
    98  	c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
    99  
   100  	input, err := c.abi.Pack("", params...)
   101  	if err != nil {
   102  		return common.Address{}, nil, nil, err
   103  	}
   104  	tx, err := c.transact(opts, nil, append(bytecode, input...))
   105  	if err != nil {
   106  		return common.Address{}, nil, nil, err
   107  	}
   108  	c.address = crypto.CreateAddress(opts.From, tx.Nonce())
   109  	return c.address, tx, c, nil
   110  }
   111  
   112  //调用调用(常量)contract方法,参数作为输入值,并且
   113  //将输出设置为结果。结果类型可能是用于
   114  //返回、匿名返回的接口切片和命名的结构
   115  //返回。
   116  func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error {
   117  //不要在懒惰的用户身上崩溃
   118  	if opts == nil {
   119  		opts = new(CallOpts)
   120  	}
   121  //打包输入,调用并解压缩结果
   122  	input, err := c.abi.Pack(method, params...)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	var (
   127  		msg    = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
   128  		ctx    = ensureContext(opts.Context)
   129  		code   []byte
   130  		output []byte
   131  	)
   132  	if opts.Pending {
   133  		pb, ok := c.caller.(PendingContractCaller)
   134  		if !ok {
   135  			return ErrNoPendingState
   136  		}
   137  		output, err = pb.PendingCallContract(ctx, msg)
   138  		if err == nil && len(output) == 0 {
   139  //确保我们有一份合同要执行,否则就要保释。
   140  			if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
   141  				return err
   142  			} else if len(code) == 0 {
   143  				return ErrNoCode
   144  			}
   145  		}
   146  	} else {
   147  		output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
   148  		if err == nil && len(output) == 0 {
   149  //确保我们有一份合同要执行,否则就要保释。
   150  			if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
   151  				return err
   152  			} else if len(code) == 0 {
   153  				return ErrNoCode
   154  			}
   155  		}
   156  	}
   157  	if err != nil {
   158  		return err
   159  	}
   160  	return c.abi.Unpack(result, method, output)
   161  }
   162  
   163  //Transact使用参数作为输入值调用(付费)Contract方法。
   164  func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
   165  //否则,打包参数并调用合同
   166  	input, err := c.abi.Pack(method, params...)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	return c.transact(opts, &c.address, input)
   171  }
   172  
   173  //转账启动普通交易以将资金转移到合同,调用
   174  //它的默认方法(如果有)。
   175  func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
   176  	return c.transact(opts, &c.address, nil)
   177  }
   178  
   179  //Transact执行实际的事务调用,首先派生任何缺少的
   180  //授权字段,然后安排事务执行。
   181  func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
   182  	var err error
   183  
   184  //确保有效的值字段并立即解析帐户
   185  	value := opts.Value
   186  	if value == nil {
   187  		value = new(big.Int)
   188  	}
   189  	var nonce uint64
   190  	if opts.Nonce == nil {
   191  		nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
   192  		if err != nil {
   193  			return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
   194  		}
   195  	} else {
   196  		nonce = opts.Nonce.Uint64()
   197  	}
   198  //计算燃气补贴和燃气价格
   199  	gasPrice := opts.GasPrice
   200  	if gasPrice == nil {
   201  		gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
   202  		if err != nil {
   203  			return nil, fmt.Errorf("failed to suggest gas price: %v", err)
   204  		}
   205  	}
   206  	gasLimit := opts.GasLimit
   207  	if gasLimit == 0 {
   208  //如果没有方法调用代码,则无法成功估计气体
   209  		if contract != nil {
   210  			if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
   211  				return nil, err
   212  			} else if len(code) == 0 {
   213  				return nil, ErrNoCode
   214  			}
   215  		}
   216  //如果合同确实有代码(或不需要代码),则估计交易
   217  		msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
   218  		gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
   219  		if err != nil {
   220  			return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
   221  		}
   222  	}
   223  //创建事务,签名并计划执行
   224  	var rawTx *types.Transaction
   225  	if contract == nil {
   226  		rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input)
   227  	} else {
   228  		rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
   229  	}
   230  	if opts.Signer == nil {
   231  		return nil, errors.New("no signer to authorize the transaction with")
   232  	}
   233  	signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
   238  		return nil, err
   239  	}
   240  	return signedTx, nil
   241  }
   242  
   243  //filterlogs过滤过去块的合同日志,返回必要的
   244  //在其上构造强类型绑定迭代器的通道。
   245  func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
   246  //不要在懒惰的用户身上崩溃
   247  	if opts == nil {
   248  		opts = new(FilterOpts)
   249  	}
   250  //将事件选择器附加到查询参数并构造主题集
   251  	query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
   252  
   253  	topics, err := makeTopics(query...)
   254  	if err != nil {
   255  		return nil, nil, err
   256  	}
   257  //启动后台筛选
   258  	logs := make(chan types.Log, 128)
   259  
   260  	config := ethereum.FilterQuery{
   261  		Addresses: []common.Address{c.address},
   262  		Topics:    topics,
   263  		FromBlock: new(big.Int).SetUint64(opts.Start),
   264  	}
   265  	if opts.End != nil {
   266  		config.ToBlock = new(big.Int).SetUint64(*opts.End)
   267  	}
   268   /*TODO(karalabe):在支持时用此替换下面方法的其余部分
   269   sub,err:=c.filter.subscribeBilterLogs(ensureContext(opts.context)、config、logs)
   270   **/
   271  
   272  	buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
   273  	if err != nil {
   274  		return nil, nil, err
   275  	}
   276  	sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
   277  		for _, log := range buff {
   278  			select {
   279  			case logs <- log:
   280  			case <-quit:
   281  				return nil
   282  			}
   283  		}
   284  		return nil
   285  	}), nil
   286  
   287  	if err != nil {
   288  		return nil, nil, err
   289  	}
   290  	return logs, sub, nil
   291  }
   292  
   293  //watchlogs过滤器订阅未来块的合同日志,返回
   294  //可用于关闭观察程序的订阅对象。
   295  func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
   296  //不要在懒惰的用户身上崩溃
   297  	if opts == nil {
   298  		opts = new(WatchOpts)
   299  	}
   300  //将事件选择器附加到查询参数并构造主题集
   301  	query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
   302  
   303  	topics, err := makeTopics(query...)
   304  	if err != nil {
   305  		return nil, nil, err
   306  	}
   307  //启动后台筛选
   308  	logs := make(chan types.Log, 128)
   309  
   310  	config := ethereum.FilterQuery{
   311  		Addresses: []common.Address{c.address},
   312  		Topics:    topics,
   313  	}
   314  	if opts.Start != nil {
   315  		config.FromBlock = new(big.Int).SetUint64(*opts.Start)
   316  	}
   317  	sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
   318  	if err != nil {
   319  		return nil, nil, err
   320  	}
   321  	return logs, sub, nil
   322  }
   323  
   324  //解包日志将检索到的日志解包到提供的输出结构中。
   325  func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
   326  	if len(log.Data) > 0 {
   327  		if err := c.abi.Unpack(out, event, log.Data); err != nil {
   328  			return err
   329  		}
   330  	}
   331  	var indexed abi.Arguments
   332  	for _, arg := range c.abi.Events[event].Inputs {
   333  		if arg.Indexed {
   334  			indexed = append(indexed, arg)
   335  		}
   336  	}
   337  	return parseTopics(out, indexed, log.Topics[1:])
   338  }
   339  
   340  //EnsureContext是一个助手方法,用于确保上下文不为零,即使
   341  //用户指定了它。
   342  func ensureContext(ctx context.Context) context.Context {
   343  	if ctx == nil {
   344  		return context.TODO()
   345  	}
   346  	return ctx
   347  }
   348