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