github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/accounts/abi/bind/base.go (about)

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