github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/contracts/chequebook/cheque.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  // Package chequebook package wraps the 'chequebook' Sberex smart contract.
    13  //
    14  // The functions in this package allow using chequebook for
    15  // issuing, receiving, verifying cheques in sbr; (auto)cashing cheques in sbr
    16  // as well as (auto)depositing sbr to the chequebook contract.
    17  package chequebook
    18  
    19  //go:generate abigen --sol contract/chequebook.sol --exc contract/mortal.sol:mortal,contract/owned.sol:owned --pkg contract --out contract/chequebook.go
    20  //go:generate go run ./gencode.go
    21  
    22  import (
    23  	"bytes"
    24  	"context"
    25  	"crypto/ecdsa"
    26  	"encoding/json"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"math/big"
    30  	"os"
    31  	"sync"
    32  	"time"
    33  
    34  	"github.com/Sberex/go-sberex/accounts/abi/bind"
    35  	"github.com/Sberex/go-sberex/common"
    36  	"github.com/Sberex/go-sberex/common/hexutil"
    37  	"github.com/Sberex/go-sberex/contracts/chequebook/contract"
    38  	"github.com/Sberex/go-sberex/core/types"
    39  	"github.com/Sberex/go-sberex/crypto"
    40  	"github.com/Sberex/go-sberex/log"
    41  	"github.com/Sberex/go-sberex/swarm/services/swap/swap"
    42  )
    43  
    44  // TODO(zelig): watch peer solvency and notify of bouncing cheques
    45  // TODO(zelig): enable paying with cheque by signing off
    46  
    47  // Some functionality requires interacting with the blockchain:
    48  // * setting current balance on peer's chequebook
    49  // * sending the transaction to cash the cheque
    50  // * depositing sbr to the chequebook
    51  // * watching incoming sbr
    52  
    53  var (
    54  	gasToCash = uint64(2000000) // gas cost of a cash transaction using chequebook
    55  	// gasToDeploy = uint64(3000000)
    56  )
    57  
    58  // Backend wraps all methods required for chequebook operation.
    59  type Backend interface {
    60  	bind.ContractBackend
    61  	TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
    62  	BalanceAt(ctx context.Context, address common.Address, blockNum *big.Int) (*big.Int, error)
    63  }
    64  
    65  // Cheque represents a payment promise to a single beneficiary.
    66  type Cheque struct {
    67  	Contract    common.Address // address of chequebook, needed to avoid cross-contract submission
    68  	Beneficiary common.Address
    69  	Amount      *big.Int // cumulative amount of all funds sent
    70  	Sig         []byte   // signature Sign(Keccak256(contract, beneficiary, amount), prvKey)
    71  }
    72  
    73  func (self *Cheque) String() string {
    74  	return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig)
    75  }
    76  
    77  type Params struct {
    78  	ContractCode, ContractAbi string
    79  }
    80  
    81  var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI}
    82  
    83  // Chequebook can create and sign cheques from a single contract to multiple beneficiaries.
    84  // It is the outgoing payment handler for peer to peer micropayments.
    85  type Chequebook struct {
    86  	path     string                      // path to chequebook file
    87  	prvKey   *ecdsa.PrivateKey           // private key to sign cheque with
    88  	lock     sync.Mutex                  //
    89  	backend  Backend                     // blockchain API
    90  	quit     chan bool                   // when closed causes autodeposit to stop
    91  	owner    common.Address              // owner address (derived from pubkey)
    92  	contract *contract.Chequebook        // abigen binding
    93  	session  *contract.ChequebookSession // abigen binding with Tx Opts
    94  
    95  	// persisted fields
    96  	balance      *big.Int                    // not synced with blockchain
    97  	contractAddr common.Address              // contract address
    98  	sent         map[common.Address]*big.Int //tallies for beneficiaries
    99  
   100  	txhash    string   // tx hash of last deposit tx
   101  	threshold *big.Int // threshold that triggers autodeposit if not nil
   102  	buffer    *big.Int // buffer to keep on top of balance for fork protection
   103  
   104  	log log.Logger // contextual logger with the contract address embedded
   105  }
   106  
   107  func (self *Chequebook) String() string {
   108  	return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey)
   109  }
   110  
   111  // NewChequebook creates a new Chequebook.
   112  func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) {
   113  	balance := new(big.Int)
   114  	sent := make(map[common.Address]*big.Int)
   115  
   116  	chbook, err := contract.NewChequebook(contractAddr, backend)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	transactOpts := bind.NewKeyedTransactor(prvKey)
   121  	session := &contract.ChequebookSession{
   122  		Contract:     chbook,
   123  		TransactOpts: *transactOpts,
   124  	}
   125  
   126  	self = &Chequebook{
   127  		prvKey:       prvKey,
   128  		balance:      balance,
   129  		contractAddr: contractAddr,
   130  		sent:         sent,
   131  		path:         path,
   132  		backend:      backend,
   133  		owner:        transactOpts.From,
   134  		contract:     chbook,
   135  		session:      session,
   136  		log:          log.New("contract", contractAddr),
   137  	}
   138  
   139  	if (contractAddr != common.Address{}) {
   140  		self.setBalanceFromBlockChain()
   141  		self.log.Trace("New chequebook initialised", "owner", self.owner, "balance", self.balance)
   142  	}
   143  	return
   144  }
   145  
   146  func (self *Chequebook) setBalanceFromBlockChain() {
   147  	balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil)
   148  	if err != nil {
   149  		log.Error("Failed to retrieve chequebook balance", "err", err)
   150  	} else {
   151  		self.balance.Set(balance)
   152  	}
   153  }
   154  
   155  // LoadChequebook loads a chequebook from disk (file path).
   156  func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) {
   157  	var data []byte
   158  	data, err = ioutil.ReadFile(path)
   159  	if err != nil {
   160  		return
   161  	}
   162  	self, _ = NewChequebook(path, common.Address{}, prvKey, backend)
   163  
   164  	err = json.Unmarshal(data, self)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	if checkBalance {
   169  		self.setBalanceFromBlockChain()
   170  	}
   171  	log.Trace("Loaded chequebook from disk", "path", path)
   172  
   173  	return
   174  }
   175  
   176  // chequebookFile is the JSON representation of a chequebook.
   177  type chequebookFile struct {
   178  	Balance  string
   179  	Contract string
   180  	Owner    string
   181  	Sent     map[string]string
   182  }
   183  
   184  // UnmarshalJSON deserialises a chequebook.
   185  func (self *Chequebook) UnmarshalJSON(data []byte) error {
   186  	var file chequebookFile
   187  	err := json.Unmarshal(data, &file)
   188  	if err != nil {
   189  		return err
   190  	}
   191  	_, ok := self.balance.SetString(file.Balance, 10)
   192  	if !ok {
   193  		return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance)
   194  	}
   195  	self.contractAddr = common.HexToAddress(file.Contract)
   196  	for addr, sent := range file.Sent {
   197  		self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
   198  		if !ok {
   199  			return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent)
   200  		}
   201  	}
   202  	return nil
   203  }
   204  
   205  // MarshalJSON serialises a chequebook.
   206  func (self *Chequebook) MarshalJSON() ([]byte, error) {
   207  	var file = &chequebookFile{
   208  		Balance:  self.balance.String(),
   209  		Contract: self.contractAddr.Hex(),
   210  		Owner:    self.owner.Hex(),
   211  		Sent:     make(map[string]string),
   212  	}
   213  	for addr, sent := range self.sent {
   214  		file.Sent[addr.Hex()] = sent.String()
   215  	}
   216  	return json.Marshal(file)
   217  }
   218  
   219  // Save persists the chequebook on disk, remembering balance, contract address and
   220  // cumulative amount of funds sent for each beneficiary.
   221  func (self *Chequebook) Save() (err error) {
   222  	data, err := json.MarshalIndent(self, "", " ")
   223  	if err != nil {
   224  		return err
   225  	}
   226  	self.log.Trace("Saving chequebook to disk", self.path)
   227  
   228  	return ioutil.WriteFile(self.path, data, os.ModePerm)
   229  }
   230  
   231  // Stop quits the autodeposit go routine to terminate
   232  func (self *Chequebook) Stop() {
   233  	defer self.lock.Unlock()
   234  	self.lock.Lock()
   235  	if self.quit != nil {
   236  		close(self.quit)
   237  		self.quit = nil
   238  	}
   239  }
   240  
   241  // Issue creates a cheque signed by the chequebook owner's private key. The
   242  // signer commits to a contract (one that they own), a beneficiary and amount.
   243  func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
   244  	defer self.lock.Unlock()
   245  	self.lock.Lock()
   246  
   247  	if amount.Sign() <= 0 {
   248  		return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
   249  	}
   250  	if self.balance.Cmp(amount) < 0 {
   251  		err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
   252  	} else {
   253  		var sig []byte
   254  		sent, found := self.sent[beneficiary]
   255  		if !found {
   256  			sent = new(big.Int)
   257  			self.sent[beneficiary] = sent
   258  		}
   259  		sum := new(big.Int).Set(sent)
   260  		sum.Add(sum, amount)
   261  
   262  		sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey)
   263  		if err == nil {
   264  			ch = &Cheque{
   265  				Contract:    self.contractAddr,
   266  				Beneficiary: beneficiary,
   267  				Amount:      sum,
   268  				Sig:         sig,
   269  			}
   270  			sent.Set(sum)
   271  			self.balance.Sub(self.balance, amount) // subtract amount from balance
   272  		}
   273  	}
   274  
   275  	// auto deposit if threshold is set and balance is less then threshold
   276  	// note this is called even if issuing cheque fails
   277  	// so we reattempt depositing
   278  	if self.threshold != nil {
   279  		if self.balance.Cmp(self.threshold) < 0 {
   280  			send := new(big.Int).Sub(self.buffer, self.balance)
   281  			self.deposit(send)
   282  		}
   283  	}
   284  
   285  	return
   286  }
   287  
   288  // Cash is a convenience method to cash any cheque.
   289  func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
   290  	return ch.Cash(self.session)
   291  }
   292  
   293  // data to sign: contract address, beneficiary, cumulative amount of funds ever sent
   294  func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte {
   295  	bigamount := sum.Bytes()
   296  	if len(bigamount) > 32 {
   297  		return nil
   298  	}
   299  	var amount32 [32]byte
   300  	copy(amount32[32-len(bigamount):32], bigamount)
   301  	input := append(contract.Bytes(), beneficiary.Bytes()...)
   302  	input = append(input, amount32[:]...)
   303  	return crypto.Keccak256(input)
   304  }
   305  
   306  // Balance returns the current balance of the chequebook.
   307  func (self *Chequebook) Balance() *big.Int {
   308  	defer self.lock.Unlock()
   309  	self.lock.Lock()
   310  	return new(big.Int).Set(self.balance)
   311  }
   312  
   313  // Owner returns the owner account of the chequebook.
   314  func (self *Chequebook) Owner() common.Address {
   315  	return self.owner
   316  }
   317  
   318  // Address returns the on-chain contract address of the chequebook.
   319  func (self *Chequebook) Address() common.Address {
   320  	return self.contractAddr
   321  }
   322  
   323  // Deposit deposits money to the chequebook account.
   324  func (self *Chequebook) Deposit(amount *big.Int) (string, error) {
   325  	defer self.lock.Unlock()
   326  	self.lock.Lock()
   327  	return self.deposit(amount)
   328  }
   329  
   330  // deposit deposits amount to the chequebook account.
   331  // The caller must hold self.lock.
   332  func (self *Chequebook) deposit(amount *big.Int) (string, error) {
   333  	// since the amount is variable here, we do not use sessions
   334  	depositTransactor := bind.NewKeyedTransactor(self.prvKey)
   335  	depositTransactor.Value = amount
   336  	chbookRaw := &contract.ChequebookRaw{Contract: self.contract}
   337  	tx, err := chbookRaw.Transfer(depositTransactor)
   338  	if err != nil {
   339  		self.log.Warn("Failed to fund chequebook", "amount", amount, "balance", self.balance, "target", self.buffer, "err", err)
   340  		return "", err
   341  	}
   342  	// assume that transaction is actually successful, we add the amount to balance right away
   343  	self.balance.Add(self.balance, amount)
   344  	self.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", self.balance, "target", self.buffer)
   345  	return tx.Hash().Hex(), nil
   346  }
   347  
   348  // AutoDeposit (re)sets interval time and amount which triggers sending funds to the
   349  // chequebook. Contract backend needs to be set if threshold is not less than buffer, then
   350  // deposit will be triggered on every new cheque issued.
   351  func (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   352  	defer self.lock.Unlock()
   353  	self.lock.Lock()
   354  	self.threshold = threshold
   355  	self.buffer = buffer
   356  	self.autoDeposit(interval)
   357  }
   358  
   359  // autoDeposit starts a goroutine that periodically sends funds to the chequebook
   360  // contract caller holds the lock the go routine terminates if Chequebook.quit is closed.
   361  func (self *Chequebook) autoDeposit(interval time.Duration) {
   362  	if self.quit != nil {
   363  		close(self.quit)
   364  		self.quit = nil
   365  	}
   366  	// if threshold >= balance autodeposit after every cheque issued
   367  	if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 {
   368  		return
   369  	}
   370  
   371  	ticker := time.NewTicker(interval)
   372  	self.quit = make(chan bool)
   373  	quit := self.quit
   374  
   375  	go func() {
   376  		for {
   377  			select {
   378  			case <-quit:
   379  				return
   380  			case <-ticker.C:
   381  				self.lock.Lock()
   382  				if self.balance.Cmp(self.buffer) < 0 {
   383  					amount := new(big.Int).Sub(self.buffer, self.balance)
   384  					txhash, err := self.deposit(amount)
   385  					if err == nil {
   386  						self.txhash = txhash
   387  					}
   388  				}
   389  				self.lock.Unlock()
   390  			}
   391  		}
   392  	}()
   393  }
   394  
   395  // Outbox can issue cheques from a single contract to a single beneficiary.
   396  type Outbox struct {
   397  	chequeBook  *Chequebook
   398  	beneficiary common.Address
   399  }
   400  
   401  // NewOutbox creates an outbox.
   402  func NewOutbox(chbook *Chequebook, beneficiary common.Address) *Outbox {
   403  	return &Outbox{chbook, beneficiary}
   404  }
   405  
   406  // Issue creates cheque.
   407  func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
   408  	return self.chequeBook.Issue(self.beneficiary, amount)
   409  }
   410  
   411  // AutoDeposit enables auto-deposits on the underlying chequebook.
   412  func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   413  	self.chequeBook.AutoDeposit(interval, threshold, buffer)
   414  }
   415  
   416  // Stop helps satisfy the swap.OutPayment interface.
   417  func (self *Outbox) Stop() {}
   418  
   419  // String implements fmt.Stringer.
   420  func (self *Outbox) String() string {
   421  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance())
   422  }
   423  
   424  // Inbox can deposit, verify and cash cheques from a single contract to a single
   425  // beneficiary. It is the incoming payment handler for peer to peer micropayments.
   426  type Inbox struct {
   427  	lock        sync.Mutex
   428  	contract    common.Address              // peer's chequebook contract
   429  	beneficiary common.Address              // local peer's receiving address
   430  	sender      common.Address              // local peer's address to send cashing tx from
   431  	signer      *ecdsa.PublicKey            // peer's public key
   432  	txhash      string                      // tx hash of last cashing tx
   433  	session     *contract.ChequebookSession // abi contract backend with tx opts
   434  	quit        chan bool                   // when closed causes autocash to stop
   435  	maxUncashed *big.Int                    // threshold that triggers autocashing
   436  	cashed      *big.Int                    // cumulative amount cashed
   437  	cheque      *Cheque                     // last cheque, nil if none yet received
   438  	log         log.Logger                  // contextual logger with the contract address embedded
   439  }
   440  
   441  // NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated
   442  // from blockchain when first cheque is received.
   443  func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (self *Inbox, err error) {
   444  	if signer == nil {
   445  		return nil, fmt.Errorf("signer is null")
   446  	}
   447  	chbook, err := contract.NewChequebook(contractAddr, abigen)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  	transactOpts := bind.NewKeyedTransactor(prvKey)
   452  	transactOpts.GasLimit = gasToCash
   453  	session := &contract.ChequebookSession{
   454  		Contract:     chbook,
   455  		TransactOpts: *transactOpts,
   456  	}
   457  	sender := transactOpts.From
   458  
   459  	self = &Inbox{
   460  		contract:    contractAddr,
   461  		beneficiary: beneficiary,
   462  		sender:      sender,
   463  		signer:      signer,
   464  		session:     session,
   465  		cashed:      new(big.Int).Set(common.Big0),
   466  		log:         log.New("contract", contractAddr),
   467  	}
   468  	self.log.Trace("New chequebook inbox initialized", "beneficiary", self.beneficiary, "signer", hexutil.Bytes(crypto.FromECDSAPub(signer)))
   469  	return
   470  }
   471  
   472  func (self *Inbox) String() string {
   473  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount)
   474  }
   475  
   476  // Stop quits the autocash goroutine.
   477  func (self *Inbox) Stop() {
   478  	defer self.lock.Unlock()
   479  	self.lock.Lock()
   480  	if self.quit != nil {
   481  		close(self.quit)
   482  		self.quit = nil
   483  	}
   484  }
   485  
   486  // Cash attempts to cash the current cheque.
   487  func (self *Inbox) Cash() (txhash string, err error) {
   488  	if self.cheque != nil {
   489  		txhash, err = self.cheque.Cash(self.session)
   490  		self.log.Trace("Cashing in chequebook cheque", "amount", self.cheque.Amount, "beneficiary", self.beneficiary)
   491  		self.cashed = self.cheque.Amount
   492  	}
   493  	return
   494  }
   495  
   496  // AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed
   497  // cheque if maxUncashed is set to 0, then autocash on receipt.
   498  func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) {
   499  	defer self.lock.Unlock()
   500  	self.lock.Lock()
   501  	self.maxUncashed = maxUncashed
   502  	self.autoCash(cashInterval)
   503  }
   504  
   505  // autoCash starts a loop that periodically clears the last cheque
   506  // if the peer is trusted. Clearing period could be 24h or a week.
   507  // The caller must hold self.lock.
   508  func (self *Inbox) autoCash(cashInterval time.Duration) {
   509  	if self.quit != nil {
   510  		close(self.quit)
   511  		self.quit = nil
   512  	}
   513  	// if maxUncashed is set to 0, then autocash on receipt
   514  	if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Sign() == 0 {
   515  		return
   516  	}
   517  
   518  	ticker := time.NewTicker(cashInterval)
   519  	self.quit = make(chan bool)
   520  	quit := self.quit
   521  
   522  	go func() {
   523  		for {
   524  			select {
   525  			case <-quit:
   526  				return
   527  			case <-ticker.C:
   528  				self.lock.Lock()
   529  				if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 {
   530  					txhash, err := self.Cash()
   531  					if err == nil {
   532  						self.txhash = txhash
   533  					}
   534  				}
   535  				self.lock.Unlock()
   536  			}
   537  		}
   538  	}()
   539  }
   540  
   541  // Receive is called to deposit the latest cheque to the incoming Inbox.
   542  // The given promise must be a *Cheque.
   543  func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
   544  	ch := promise.(*Cheque)
   545  
   546  	defer self.lock.Unlock()
   547  	self.lock.Lock()
   548  
   549  	var sum *big.Int
   550  	if self.cheque == nil {
   551  		// the sum is checked against the blockchain once a cheque is received
   552  		tally, err := self.session.Sent(self.beneficiary)
   553  		if err != nil {
   554  			return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err)
   555  		}
   556  		sum = tally
   557  	} else {
   558  		sum = self.cheque.Amount
   559  	}
   560  
   561  	amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum)
   562  	var uncashed *big.Int
   563  	if err == nil {
   564  		self.cheque = ch
   565  
   566  		if self.maxUncashed != nil {
   567  			uncashed = new(big.Int).Sub(ch.Amount, self.cashed)
   568  			if self.maxUncashed.Cmp(uncashed) < 0 {
   569  				self.Cash()
   570  			}
   571  		}
   572  		self.log.Trace("Received cheque in chequebook inbox", "amount", amount, "uncashed", uncashed)
   573  	}
   574  
   575  	return amount, err
   576  }
   577  
   578  // Verify verifies cheque for signer, contract, beneficiary, amount, valid signature.
   579  func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
   580  	log.Trace("Verifying chequebook cheque", "cheque", self, "sum", sum)
   581  	if sum == nil {
   582  		return nil, fmt.Errorf("invalid amount")
   583  	}
   584  
   585  	if self.Beneficiary != beneficiary {
   586  		return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex())
   587  	}
   588  	if self.Contract != contract {
   589  		return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex())
   590  	}
   591  
   592  	amount := new(big.Int).Set(self.Amount)
   593  	if sum != nil {
   594  		amount.Sub(amount, sum)
   595  		if amount.Sign() <= 0 {
   596  			return nil, fmt.Errorf("incorrect amount: %v <= 0", amount)
   597  		}
   598  	}
   599  
   600  	pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.Sig)
   601  	if err != nil {
   602  		return nil, fmt.Errorf("invalid signature: %v", err)
   603  	}
   604  	if !bytes.Equal(crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) {
   605  		return nil, fmt.Errorf("signer mismatch: %x != %x", crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey))
   606  	}
   607  	return amount, nil
   608  }
   609  
   610  // v/r/s representation of signature
   611  func sig2vrs(sig []byte) (v byte, r, s [32]byte) {
   612  	v = sig[64] + 27
   613  	copy(r[:], sig[:32])
   614  	copy(s[:], sig[32:64])
   615  	return
   616  }
   617  
   618  // Cash cashes the cheque by sending an Sberex transaction.
   619  func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
   620  	v, r, s := sig2vrs(self.Sig)
   621  	tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s)
   622  	if err != nil {
   623  		return "", err
   624  	}
   625  	return tx.Hash().Hex(), nil
   626  }
   627  
   628  // ValidateCode checks that the on-chain code at address matches the expected chequebook
   629  // contract code. This is used to detect suicided chequebooks.
   630  func ValidateCode(ctx context.Context, b Backend, address common.Address) (ok bool, err error) {
   631  	code, err := b.CodeAt(ctx, address, nil)
   632  	if err != nil {
   633  		return false, err
   634  	}
   635  	return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil
   636  }