github.com/bamzi/go-ethereum@v1.6.7-0.20170704111104-138f26c93af1/contracts/chequebook/cheque.go (about)

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