github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/contracts/chequebook/cheque.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  //版权所有2016 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  //包裹支票簿包裹“支票簿”以太坊智能合约。
    26  //
    27  //此包中的函数允许使用支票簿
    28  //在以太网上签发、接收、验证支票;(自动)在以太网上兑现支票
    29  //以及(自动)将以太存入支票簿合同。
    30  package chequebook
    31  
    32  //go:生成abigen--sol contract/checkbook.sol--exc contract/凡人.sol:凡人,contract/owned.sol:owned--pkg contract--out contract/checkbook.go
    33  //go:生成go run./gencode.go
    34  
    35  import (
    36  	"bytes"
    37  	"context"
    38  	"crypto/ecdsa"
    39  	"encoding/json"
    40  	"fmt"
    41  	"io/ioutil"
    42  	"math/big"
    43  	"os"
    44  	"sync"
    45  	"time"
    46  
    47  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    48  	"github.com/ethereum/go-ethereum/common"
    49  	"github.com/ethereum/go-ethereum/common/hexutil"
    50  	"github.com/ethereum/go-ethereum/contracts/chequebook/contract"
    51  	"github.com/ethereum/go-ethereum/core/types"
    52  	"github.com/ethereum/go-ethereum/crypto"
    53  	"github.com/ethereum/go-ethereum/log"
    54  	"github.com/ethereum/go-ethereum/swarm/services/swap/swap"
    55  )
    56  
    57  //托多(泽利格):观察同龄人的偿付能力,并通知跳票
    58  //TODO(Zelig):通过签核启用支票付款
    59  
    60  //有些功能需要与区块链交互:
    61  //*设置对等支票簿的当前余额
    62  //*发送交易以兑现支票
    63  //*将乙醚存入支票簿
    64  //*观察进入的乙醚
    65  
    66  var (
    67  gasToCash = uint64(2000000) //使用支票簿的现金交易的天然气成本
    68  //
    69  )
    70  
    71  //后端包装支票簿操作所需的所有方法。
    72  type Backend interface {
    73  	bind.ContractBackend
    74  	TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
    75  	BalanceAt(ctx context.Context, address common.Address, blockNum *big.Int) (*big.Int, error)
    76  }
    77  
    78  //支票代表对单一受益人的付款承诺。
    79  type Cheque struct {
    80  Contract    common.Address //支票簿地址,避免交叉合同提交
    81  	Beneficiary common.Address
    82  Amount      *big.Int //所有已发送资金的累计金额
    83  Sig         []byte   //签字(KECCAK256(合同、受益人、金额)、prvkey)
    84  }
    85  
    86  func (self *Cheque) String() string {
    87  	return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig)
    88  }
    89  
    90  type Params struct {
    91  	ContractCode, ContractAbi string
    92  }
    93  
    94  var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI}
    95  
    96  //支票簿可以创建并签署从单个合同到多个受益人的支票。
    97  //它是对等小额支付的传出支付处理程序。
    98  type Chequebook struct {
    99  path     string                      //支票簿文件路径
   100  prvKey   *ecdsa.PrivateKey           //用于签署支票的私钥
   101  lock     sync.Mutex                  //
   102  backend  Backend                     //块链API
   103  quit     chan bool                   //关闭时会导致自动报告停止
   104  owner    common.Address              //所有者地址(从pubkey派生)
   105  contract *contract.Chequebook        //非本征结合
   106  session  *contract.ChequebookSession //Abigen与Tx OPT结合
   107  
   108  //保留字段
   109  balance      *big.Int                    //
   110  contractAddr common.Address              //合同地址
   111  sent         map[common.Address]*big.Int //受益人计数
   112  
   113  txhash    string   //上次存款的Tx哈希Tx
   114  threshold *big.Int //如果不是零,则触发自动报告的阈值
   115  buffer    *big.Int //保持平衡的缓冲器,用于叉保护
   116  
   117  log log.Logger //嵌入合同地址的上下文记录器
   118  }
   119  
   120  func (self *Chequebook) String() string {
   121  	return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey)
   122  }
   123  
   124  //
   125  func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) {
   126  	balance := new(big.Int)
   127  	sent := make(map[common.Address]*big.Int)
   128  
   129  	chbook, err := contract.NewChequebook(contractAddr, backend)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	transactOpts := bind.NewKeyedTransactor(prvKey)
   134  	session := &contract.ChequebookSession{
   135  		Contract:     chbook,
   136  		TransactOpts: *transactOpts,
   137  	}
   138  
   139  	self = &Chequebook{
   140  		prvKey:       prvKey,
   141  		balance:      balance,
   142  		contractAddr: contractAddr,
   143  		sent:         sent,
   144  		path:         path,
   145  		backend:      backend,
   146  		owner:        transactOpts.From,
   147  		contract:     chbook,
   148  		session:      session,
   149  		log:          log.New("contract", contractAddr),
   150  	}
   151  
   152  	if (contractAddr != common.Address{}) {
   153  		self.setBalanceFromBlockChain()
   154  		self.log.Trace("New chequebook initialised", "owner", self.owner, "balance", self.balance)
   155  	}
   156  	return
   157  }
   158  
   159  func (self *Chequebook) setBalanceFromBlockChain() {
   160  	balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil)
   161  	if err != nil {
   162  		log.Error("Failed to retrieve chequebook balance", "err", err)
   163  	} else {
   164  		self.balance.Set(balance)
   165  	}
   166  }
   167  
   168  //LoadChequebook loads a chequebook from disk (file path).
   169  func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) {
   170  	var data []byte
   171  	data, err = ioutil.ReadFile(path)
   172  	if err != nil {
   173  		return
   174  	}
   175  	self, _ = NewChequebook(path, common.Address{}, prvKey, backend)
   176  
   177  	err = json.Unmarshal(data, self)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	if checkBalance {
   182  		self.setBalanceFromBlockChain()
   183  	}
   184  	log.Trace("Loaded chequebook from disk", "path", path)
   185  
   186  	return
   187  }
   188  
   189  //支票簿文件是支票簿的JSON表示。
   190  type chequebookFile struct {
   191  	Balance  string
   192  	Contract string
   193  	Owner    string
   194  	Sent     map[string]string
   195  }
   196  
   197  //取消对支票簿的反序列化。
   198  func (self *Chequebook) UnmarshalJSON(data []byte) error {
   199  	var file chequebookFile
   200  	err := json.Unmarshal(data, &file)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	_, ok := self.balance.SetString(file.Balance, 10)
   205  	if !ok {
   206  		return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance)
   207  	}
   208  	self.contractAddr = common.HexToAddress(file.Contract)
   209  	for addr, sent := range file.Sent {
   210  		self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
   211  		if !ok {
   212  			return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent)
   213  		}
   214  	}
   215  	return nil
   216  }
   217  
   218  //Marshaljson将支票簿序列化。
   219  func (self *Chequebook) MarshalJSON() ([]byte, error) {
   220  	var file = &chequebookFile{
   221  		Balance:  self.balance.String(),
   222  		Contract: self.contractAddr.Hex(),
   223  		Owner:    self.owner.Hex(),
   224  		Sent:     make(map[string]string),
   225  	}
   226  	for addr, sent := range self.sent {
   227  		file.Sent[addr.Hex()] = sent.String()
   228  	}
   229  	return json.Marshal(file)
   230  }
   231  
   232  //保存将支票簿保存在磁盘上,记住余额、合同地址和
   233  //
   234  func (self *Chequebook) Save() (err error) {
   235  	data, err := json.MarshalIndent(self, "", " ")
   236  	if err != nil {
   237  		return err
   238  	}
   239  	self.log.Trace("Saving chequebook to disk", self.path)
   240  
   241  	return ioutil.WriteFile(self.path, data, os.ModePerm)
   242  }
   243  
   244  //Stop退出自动报告Go例程以终止
   245  func (self *Chequebook) Stop() {
   246  	defer self.lock.Unlock()
   247  	self.lock.Lock()
   248  	if self.quit != nil {
   249  		close(self.quit)
   250  		self.quit = nil
   251  	}
   252  }
   253  
   254  //发行创建由支票簿所有者的私钥签名的支票。这个
   255  //签字人承诺合同(他们拥有的),受益人和金额。
   256  func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
   257  	defer self.lock.Unlock()
   258  	self.lock.Lock()
   259  
   260  	if amount.Sign() <= 0 {
   261  		return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
   262  	}
   263  	if self.balance.Cmp(amount) < 0 {
   264  		err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
   265  	} else {
   266  		var sig []byte
   267  		sent, found := self.sent[beneficiary]
   268  		if !found {
   269  			sent = new(big.Int)
   270  			self.sent[beneficiary] = sent
   271  		}
   272  		sum := new(big.Int).Set(sent)
   273  		sum.Add(sum, amount)
   274  
   275  		sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey)
   276  		if err == nil {
   277  			ch = &Cheque{
   278  				Contract:    self.contractAddr,
   279  				Beneficiary: beneficiary,
   280  				Amount:      sum,
   281  				Sig:         sig,
   282  			}
   283  			sent.Set(sum)
   284  self.balance.Sub(self.balance, amount) //从余额中减去金额
   285  		}
   286  	}
   287  
   288  //如果设置了阈值且余额小于阈值,则自动存款
   289  //请注意,即使签发支票失败,也会调用此函数。
   290  //所以我们重新尝试存放
   291  	if self.threshold != nil {
   292  		if self.balance.Cmp(self.threshold) < 0 {
   293  			send := new(big.Int).Sub(self.buffer, self.balance)
   294  			self.deposit(send)
   295  		}
   296  	}
   297  
   298  	return
   299  }
   300  
   301  //现金是兑现支票的一种方便方法。
   302  func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
   303  	return ch.Cash(self.session)
   304  }
   305  
   306  //签署资料:合同地址、受益人、累计发送资金金额
   307  func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte {
   308  	bigamount := sum.Bytes()
   309  	if len(bigamount) > 32 {
   310  		return nil
   311  	}
   312  	var amount32 [32]byte
   313  	copy(amount32[32-len(bigamount):32], bigamount)
   314  	input := append(contract.Bytes(), beneficiary.Bytes()...)
   315  	input = append(input, amount32[:]...)
   316  	return crypto.Keccak256(input)
   317  }
   318  
   319  //余额返回支票簿的当前余额。
   320  func (self *Chequebook) Balance() *big.Int {
   321  	defer self.lock.Unlock()
   322  	self.lock.Lock()
   323  	return new(big.Int).Set(self.balance)
   324  }
   325  
   326  //所有者返回支票簿的所有者帐户。
   327  func (self *Chequebook) Owner() common.Address {
   328  	return self.owner
   329  }
   330  
   331  //地址返回支票簿的链上合同地址。
   332  func (self *Chequebook) Address() common.Address {
   333  	return self.contractAddr
   334  }
   335  
   336  //把钱存入支票簿帐户。
   337  func (self *Chequebook) Deposit(amount *big.Int) (string, error) {
   338  	defer self.lock.Unlock()
   339  	self.lock.Lock()
   340  	return self.deposit(amount)
   341  }
   342  
   343  //存款金额到支票簿帐户。
   344  //调用方必须保持self.lock。
   345  func (self *Chequebook) deposit(amount *big.Int) (string, error) {
   346  //因为这里的金额是可变的,所以我们不使用会话
   347  	depositTransactor := bind.NewKeyedTransactor(self.prvKey)
   348  	depositTransactor.Value = amount
   349  	chbookRaw := &contract.ChequebookRaw{Contract: self.contract}
   350  	tx, err := chbookRaw.Transfer(depositTransactor)
   351  	if err != nil {
   352  		self.log.Warn("Failed to fund chequebook", "amount", amount, "balance", self.balance, "target", self.buffer, "err", err)
   353  		return "", err
   354  	}
   355  //假设交易实际成功,我们立即添加余额。
   356  	self.balance.Add(self.balance, amount)
   357  	self.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", self.balance, "target", self.buffer)
   358  	return tx.Hash().Hex(), nil
   359  }
   360  
   361  //autodeposit(re)设置触发向
   362  //支票簿。如果阈值不小于缓冲区,则需要设置合同后端,然后
   363  //每一张新支票都会触发存款。
   364  func (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   365  	defer self.lock.Unlock()
   366  	self.lock.Lock()
   367  	self.threshold = threshold
   368  	self.buffer = buffer
   369  	self.autoDeposit(interval)
   370  }
   371  
   372  //自动报告启动一个定期向支票簿发送资金的goroutine
   373  //如果checkbook.quit已关闭,则合同调用方将持有执行例程终止的锁。
   374  func (self *Chequebook) autoDeposit(interval time.Duration) {
   375  	if self.quit != nil {
   376  		close(self.quit)
   377  		self.quit = nil
   378  	}
   379  //如果阈值大于等于每次签发支票后自动保存余额
   380  	if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 {
   381  		return
   382  	}
   383  
   384  	ticker := time.NewTicker(interval)
   385  	self.quit = make(chan bool)
   386  	quit := self.quit
   387  
   388  	go func() {
   389  		for {
   390  			select {
   391  			case <-quit:
   392  				return
   393  			case <-ticker.C:
   394  				self.lock.Lock()
   395  				if self.balance.Cmp(self.buffer) < 0 {
   396  					amount := new(big.Int).Sub(self.buffer, self.balance)
   397  					txhash, err := self.deposit(amount)
   398  					if err == nil {
   399  						self.txhash = txhash
   400  					}
   401  				}
   402  				self.lock.Unlock()
   403  			}
   404  		}
   405  	}()
   406  }
   407  
   408  //发件箱可以向单个受益人签发来自单个合同的支票。
   409  type Outbox struct {
   410  	chequeBook  *Chequebook
   411  	beneficiary common.Address
   412  }
   413  
   414  //新发件箱创建发件箱。
   415  func NewOutbox(chbook *Chequebook, beneficiary common.Address) *Outbox {
   416  	return &Outbox{chbook, beneficiary}
   417  }
   418  
   419  //发行创建支票。
   420  func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
   421  	return self.chequeBook.Issue(self.beneficiary, amount)
   422  }
   423  
   424  //自动存单可以在基础支票簿上自动存款。
   425  func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   426  	self.chequeBook.AutoDeposit(interval, threshold, buffer)
   427  }
   428  
   429  //stop帮助满足swap.outpayment接口。
   430  func (self *Outbox) Stop() {}
   431  
   432  //字符串实现fmt.stringer。
   433  func (self *Outbox) String() string {
   434  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance())
   435  }
   436  
   437  //收件箱可以将单个合同中的支票存入、验证和兑现为单个合同中的支票。
   438  //受益人。它是对等小额支付的传入支付处理程序。
   439  type Inbox struct {
   440  	lock        sync.Mutex
   441  contract    common.Address              //同行支票簿合同
   442  beneficiary common.Address              //本地对等机的接收地址
   443  sender      common.Address              //要从中发送兑现发送信息的本地对等方地址
   444  signer      *ecdsa.PublicKey            //对等方的公钥
   445  txhash      string                      //上次兑现的Tx哈希Tx
   446  session     *contract.ChequebookSession //ABI与TX OPT的后端合同
   447  quit        chan bool                   //当关闭时,自动灰烬停止
   448  maxUncashed *big.Int                    //
   449  cashed      *big.Int                    //累计兑现金额
   450  cheque      *Cheque                     //最后一张支票,如果没有收到,则为零。
   451  log         log.Logger                  //嵌入合同地址的上下文记录器
   452  }
   453  
   454  //newinbox创建一个收件箱。未持久化收件箱,将更新累积和
   455  //收到第一张支票时从区块链获取。
   456  func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (self *Inbox, err error) {
   457  	if signer == nil {
   458  		return nil, fmt.Errorf("signer is null")
   459  	}
   460  	chbook, err := contract.NewChequebook(contractAddr, abigen)
   461  	if err != nil {
   462  		return nil, err
   463  	}
   464  	transactOpts := bind.NewKeyedTransactor(prvKey)
   465  	transactOpts.GasLimit = gasToCash
   466  	session := &contract.ChequebookSession{
   467  		Contract:     chbook,
   468  		TransactOpts: *transactOpts,
   469  	}
   470  	sender := transactOpts.From
   471  
   472  	self = &Inbox{
   473  		contract:    contractAddr,
   474  		beneficiary: beneficiary,
   475  		sender:      sender,
   476  		signer:      signer,
   477  		session:     session,
   478  		cashed:      new(big.Int).Set(common.Big0),
   479  		log:         log.New("contract", contractAddr),
   480  	}
   481  	self.log.Trace("New chequebook inbox initialized", "beneficiary", self.beneficiary, "signer", hexutil.Bytes(crypto.FromECDSAPub(signer)))
   482  	return
   483  }
   484  
   485  func (self *Inbox) String() string {
   486  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount)
   487  }
   488  
   489  //停止退出自动清除引擎。
   490  func (self *Inbox) Stop() {
   491  	defer self.lock.Unlock()
   492  	self.lock.Lock()
   493  	if self.quit != nil {
   494  		close(self.quit)
   495  		self.quit = nil
   496  	}
   497  }
   498  
   499  //现金试图兑现当前支票。
   500  func (self *Inbox) Cash() (txhash string, err error) {
   501  	if self.cheque != nil {
   502  		txhash, err = self.cheque.Cash(self.session)
   503  		self.log.Trace("Cashing in chequebook cheque", "amount", self.cheque.Amount, "beneficiary", self.beneficiary)
   504  		self.cashed = self.cheque.Amount
   505  	}
   506  	return
   507  }
   508  
   509  //autocash(re)设置触发上次未兑现兑现的最大时间和金额。
   510  //如果maxuncashed设置为0,则在收到时自动清除。
   511  func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) {
   512  	defer self.lock.Unlock()
   513  	self.lock.Lock()
   514  	self.maxUncashed = maxUncashed
   515  	self.autoCash(cashInterval)
   516  }
   517  
   518  //autocash启动一个循环,周期性地清除最后一张支票。
   519  //如果对等方是可信的。清理时间可以是24小时或一周。
   520  //调用方必须保持self.lock。
   521  func (self *Inbox) autoCash(cashInterval time.Duration) {
   522  	if self.quit != nil {
   523  		close(self.quit)
   524  		self.quit = nil
   525  	}
   526  //如果maxuncashed设置为0,则在接收时自动清除
   527  	if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Sign() == 0 {
   528  		return
   529  	}
   530  
   531  	ticker := time.NewTicker(cashInterval)
   532  	self.quit = make(chan bool)
   533  	quit := self.quit
   534  
   535  	go func() {
   536  		for {
   537  			select {
   538  			case <-quit:
   539  				return
   540  			case <-ticker.C:
   541  				self.lock.Lock()
   542  				if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 {
   543  					txhash, err := self.Cash()
   544  					if err == nil {
   545  						self.txhash = txhash
   546  					}
   547  				}
   548  				self.lock.Unlock()
   549  			}
   550  		}
   551  	}()
   552  }
   553  
   554  //Receive被调用将最新的支票存入接收的收件箱。
   555  //给出的承诺必须是一张*支票。
   556  func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
   557  	ch := promise.(*Cheque)
   558  
   559  	defer self.lock.Unlock()
   560  	self.lock.Lock()
   561  
   562  	var sum *big.Int
   563  	if self.cheque == nil {
   564  //一旦收到支票,金额将与区块链核对。
   565  		tally, err := self.session.Sent(self.beneficiary)
   566  		if err != nil {
   567  			return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err)
   568  		}
   569  		sum = tally
   570  	} else {
   571  		sum = self.cheque.Amount
   572  	}
   573  
   574  	amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum)
   575  	var uncashed *big.Int
   576  	if err == nil {
   577  		self.cheque = ch
   578  
   579  		if self.maxUncashed != nil {
   580  			uncashed = new(big.Int).Sub(ch.Amount, self.cashed)
   581  			if self.maxUncashed.Cmp(uncashed) < 0 {
   582  				self.Cash()
   583  			}
   584  		}
   585  		self.log.Trace("Received cheque in chequebook inbox", "amount", amount, "uncashed", uncashed)
   586  	}
   587  
   588  	return amount, err
   589  }
   590  
   591  //核实支票的签字人、合同人、受益人、金额、有效签字。
   592  func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
   593  	log.Trace("Verifying chequebook cheque", "cheque", self, "sum", sum)
   594  	if sum == nil {
   595  		return nil, fmt.Errorf("invalid amount")
   596  	}
   597  
   598  	if self.Beneficiary != beneficiary {
   599  		return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex())
   600  	}
   601  	if self.Contract != contract {
   602  		return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex())
   603  	}
   604  
   605  	amount := new(big.Int).Set(self.Amount)
   606  	if sum != nil {
   607  		amount.Sub(amount, sum)
   608  		if amount.Sign() <= 0 {
   609  			return nil, fmt.Errorf("incorrect amount: %v <= 0", amount)
   610  		}
   611  	}
   612  
   613  	pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.Sig)
   614  	if err != nil {
   615  		return nil, fmt.Errorf("invalid signature: %v", err)
   616  	}
   617  	if !bytes.Equal(crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) {
   618  		return nil, fmt.Errorf("signer mismatch: %x != %x", crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey))
   619  	}
   620  	return amount, nil
   621  }
   622  
   623  //签名的V/R/S表示
   624  func sig2vrs(sig []byte) (v byte, r, s [32]byte) {
   625  	v = sig[64] + 27
   626  	copy(r[:], sig[:32])
   627  	copy(s[:], sig[32:64])
   628  	return
   629  }
   630  
   631  //现金通过发送以太坊交易来兑现支票。
   632  func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
   633  	v, r, s := sig2vrs(self.Sig)
   634  	tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s)
   635  	if err != nil {
   636  		return "", err
   637  	}
   638  	return tx.Hash().Hex(), nil
   639  }
   640  
   641  //validatecode检查地址处的链上代码是否与预期的支票簿匹配
   642  //合同代码。这用于检测自杀支票簿。
   643  func ValidateCode(ctx context.Context, b Backend, address common.Address) (ok bool, err error) {
   644  	code, err := b.CodeAt(ctx, address, nil)
   645  	if err != nil {
   646  		return false, err
   647  	}
   648  	return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil
   649  }