github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/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 --exc contract/mortal.sol:mortal,contract/owned.sol:owned --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 = uint64(2000000) // gas cost of a cash transaction using chequebook
    60  	// gasToDeploy = uint64(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 (ch *Cheque) String() string {
    79  	return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", ch.Contract.Hex(), ch.Beneficiary.Hex(), ch.Amount, ch.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 (cb *Chequebook) String() string {
   113  	return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", cb.contractAddr.Hex(), cb.owner.Hex(), cb.balance, cb.prvKey.PublicKey)
   114  }
   115  
   116  // NewChequebook creates a new Chequebook.
   117  func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (*Chequebook, 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  	cb := &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  	if (contractAddr != common.Address{}) {
   144  		cb.setBalanceFromBlockChain()
   145  		cb.log.Trace("New chequebook initialised", "owner", cb.owner, "balance", cb.balance)
   146  	}
   147  	return cb, nil
   148  }
   149  
   150  func (cb *Chequebook) setBalanceFromBlockChain() {
   151  	balance, err := cb.backend.BalanceAt(context.TODO(), cb.contractAddr, nil)
   152  	if err != nil {
   153  		log.Error("Failed to retrieve chequebook balance", "err", err)
   154  	} else {
   155  		cb.balance.Set(balance)
   156  	}
   157  }
   158  
   159  // LoadChequebook loads a chequebook from disk (file path).
   160  func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (*Chequebook, error) {
   161  	data, err := ioutil.ReadFile(path)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	cb, _ := NewChequebook(path, common.Address{}, prvKey, backend)
   166  
   167  	if err = json.Unmarshal(data, cb); err != nil {
   168  		return nil, err
   169  	}
   170  	if checkBalance {
   171  		cb.setBalanceFromBlockChain()
   172  	}
   173  	log.Trace("Loaded chequebook from disk", "path", path)
   174  
   175  	return cb, nil
   176  }
   177  
   178  // chequebookFile is the JSON representation of a chequebook.
   179  type chequebookFile struct {
   180  	Balance  string
   181  	Contract string
   182  	Owner    string
   183  	Sent     map[string]string
   184  }
   185  
   186  // UnmarshalJSON deserialises a chequebook.
   187  func (cb *Chequebook) UnmarshalJSON(data []byte) error {
   188  	var file chequebookFile
   189  	err := json.Unmarshal(data, &file)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	_, ok := cb.balance.SetString(file.Balance, 10)
   194  	if !ok {
   195  		return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance)
   196  	}
   197  	cb.contractAddr = common.HexToAddress(file.Contract)
   198  	for addr, sent := range file.Sent {
   199  		cb.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
   200  		if !ok {
   201  			return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent)
   202  		}
   203  	}
   204  	return nil
   205  }
   206  
   207  // MarshalJSON serialises a chequebook.
   208  func (cb *Chequebook) MarshalJSON() ([]byte, error) {
   209  	var file = &chequebookFile{
   210  		Balance:  cb.balance.String(),
   211  		Contract: cb.contractAddr.Hex(),
   212  		Owner:    cb.owner.Hex(),
   213  		Sent:     make(map[string]string),
   214  	}
   215  	for addr, sent := range cb.sent {
   216  		file.Sent[addr.Hex()] = sent.String()
   217  	}
   218  	return json.Marshal(file)
   219  }
   220  
   221  // Save persists the chequebook on disk, remembering balance, contract address and
   222  // cumulative amount of funds sent for each beneficiary.
   223  func (cb *Chequebook) Save() error {
   224  	data, err := json.MarshalIndent(cb, "", " ")
   225  	if err != nil {
   226  		return err
   227  	}
   228  	cb.log.Trace("Saving chequebook to disk", cb.path)
   229  
   230  	return ioutil.WriteFile(cb.path, data, os.ModePerm)
   231  }
   232  
   233  // Stop quits the autodeposit go routine to terminate
   234  func (cb *Chequebook) Stop() {
   235  	defer cb.lock.Unlock()
   236  	cb.lock.Lock()
   237  	if cb.quit != nil {
   238  		close(cb.quit)
   239  		cb.quit = nil
   240  	}
   241  }
   242  
   243  // Issue creates a cheque signed by the chequebook owner's private key. The
   244  // signer commits to a contract (one that they own), a beneficiary and amount.
   245  func (cb *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (*Cheque, error) {
   246  	defer cb.lock.Unlock()
   247  	cb.lock.Lock()
   248  
   249  	if amount.Sign() <= 0 {
   250  		return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
   251  	}
   252  	var (
   253  		ch  *Cheque
   254  		err error
   255  	)
   256  	if cb.balance.Cmp(amount) < 0 {
   257  		err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, cb.balance)
   258  	} else {
   259  		var sig []byte
   260  		sent, found := cb.sent[beneficiary]
   261  		if !found {
   262  			sent = new(big.Int)
   263  			cb.sent[beneficiary] = sent
   264  		}
   265  		sum := new(big.Int).Set(sent)
   266  		sum.Add(sum, amount)
   267  
   268  		sig, err = crypto.Sign(sigHash(cb.contractAddr, beneficiary, sum), cb.prvKey)
   269  		if err == nil {
   270  			ch = &Cheque{
   271  				Contract:    cb.contractAddr,
   272  				Beneficiary: beneficiary,
   273  				Amount:      sum,
   274  				Sig:         sig,
   275  			}
   276  			sent.Set(sum)
   277  			cb.balance.Sub(cb.balance, amount) // subtract amount from balance
   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 cb.threshold != nil {
   284  		if cb.balance.Cmp(cb.threshold) < 0 {
   285  			send := new(big.Int).Sub(cb.buffer, cb.balance)
   286  			cb.deposit(send)
   287  		}
   288  	}
   289  	return ch, err
   290  }
   291  
   292  // Cash is a convenience method to cash any cheque.
   293  func (cb *Chequebook) Cash(ch *Cheque) (string, error) {
   294  	return ch.Cash(cb.session)
   295  }
   296  
   297  // data to sign: contract address, beneficiary, cumulative amount of funds ever sent
   298  func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte {
   299  	bigamount := sum.Bytes()
   300  	if len(bigamount) > 32 {
   301  		return nil
   302  	}
   303  	var amount32 [32]byte
   304  	copy(amount32[32-len(bigamount):32], bigamount)
   305  	input := append(contract.Bytes(), beneficiary.Bytes()...)
   306  	input = append(input, amount32[:]...)
   307  	return crypto.Keccak256(input)
   308  }
   309  
   310  // Balance returns the current balance of the chequebook.
   311  func (cb *Chequebook) Balance() *big.Int {
   312  	defer cb.lock.Unlock()
   313  	cb.lock.Lock()
   314  	return new(big.Int).Set(cb.balance)
   315  }
   316  
   317  // Owner returns the owner account of the chequebook.
   318  func (cb *Chequebook) Owner() common.Address {
   319  	return cb.owner
   320  }
   321  
   322  // Address returns the on-chain contract address of the chequebook.
   323  func (cb *Chequebook) Address() common.Address {
   324  	return cb.contractAddr
   325  }
   326  
   327  // Deposit deposits money to the chequebook account.
   328  func (cb *Chequebook) Deposit(amount *big.Int) (string, error) {
   329  	defer cb.lock.Unlock()
   330  	cb.lock.Lock()
   331  	return cb.deposit(amount)
   332  }
   333  
   334  // deposit deposits amount to the chequebook account.
   335  // The caller must hold lock.
   336  func (cb *Chequebook) deposit(amount *big.Int) (string, error) {
   337  	// since the amount is variable here, we do not use sessions
   338  	depositTransactor := bind.NewKeyedTransactor(cb.prvKey)
   339  	depositTransactor.Value = amount
   340  	chbookRaw := &contract.ChequebookRaw{Contract: cb.contract}
   341  	tx, err := chbookRaw.Transfer(depositTransactor)
   342  	if err != nil {
   343  		cb.log.Warn("Failed to fund chequebook", "amount", amount, "balance", cb.balance, "target", cb.buffer, "err", err)
   344  		return "", err
   345  	}
   346  	// assume that transaction is actually successful, we add the amount to balance right away
   347  	cb.balance.Add(cb.balance, amount)
   348  	cb.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", cb.balance, "target", cb.buffer)
   349  	return tx.Hash().Hex(), nil
   350  }
   351  
   352  // AutoDeposit (re)sets interval time and amount which triggers sending funds to the
   353  // chequebook. Contract backend needs to be set if threshold is not less than buffer, then
   354  // deposit will be triggered on every new cheque issued.
   355  func (cb *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   356  	defer cb.lock.Unlock()
   357  	cb.lock.Lock()
   358  	cb.threshold = threshold
   359  	cb.buffer = buffer
   360  	cb.autoDeposit(interval)
   361  }
   362  
   363  // autoDeposit starts a goroutine that periodically sends funds to the chequebook
   364  // contract caller holds the lock the go routine terminates if Chequebook.quit is closed.
   365  func (cb *Chequebook) autoDeposit(interval time.Duration) {
   366  	if cb.quit != nil {
   367  		close(cb.quit)
   368  		cb.quit = nil
   369  	}
   370  	// if threshold >= balance autodeposit after every cheque issued
   371  	if interval == time.Duration(0) || cb.threshold != nil && cb.buffer != nil && cb.threshold.Cmp(cb.buffer) >= 0 {
   372  		return
   373  	}
   374  
   375  	ticker := time.NewTicker(interval)
   376  	cb.quit = make(chan bool)
   377  	quit := cb.quit
   378  
   379  	go func() {
   380  		for {
   381  			select {
   382  			case <-quit:
   383  				return
   384  			case <-ticker.C:
   385  				cb.lock.Lock()
   386  				if cb.balance.Cmp(cb.buffer) < 0 {
   387  					amount := new(big.Int).Sub(cb.buffer, cb.balance)
   388  					txhash, err := cb.deposit(amount)
   389  					if err == nil {
   390  						cb.txhash = txhash
   391  					}
   392  				}
   393  				cb.lock.Unlock()
   394  			}
   395  		}
   396  	}()
   397  }
   398  
   399  // Outbox can issue cheques from a single contract to a single beneficiary.
   400  type Outbox struct {
   401  	chequeBook  *Chequebook
   402  	beneficiary common.Address
   403  }
   404  
   405  // NewOutbox creates an outbox.
   406  func NewOutbox(cb *Chequebook, beneficiary common.Address) *Outbox {
   407  	return &Outbox{cb, beneficiary}
   408  }
   409  
   410  // Issue creates cheque.
   411  func (o *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
   412  	return o.chequeBook.Issue(o.beneficiary, amount)
   413  }
   414  
   415  // AutoDeposit enables auto-deposits on the underlying chequebook.
   416  func (o *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   417  	o.chequeBook.AutoDeposit(interval, threshold, buffer)
   418  }
   419  
   420  // Stop helps satisfy the swap.OutPayment interface.
   421  func (o *Outbox) Stop() {}
   422  
   423  // String implements fmt.Stringer.
   424  func (o *Outbox) String() string {
   425  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", o.chequeBook.Address().Hex(), o.beneficiary.Hex(), o.chequeBook.Balance())
   426  }
   427  
   428  // Inbox can deposit, verify and cash cheques from a single contract to a single
   429  // beneficiary. It is the incoming payment handler for peer to peer micropayments.
   430  type Inbox struct {
   431  	lock        sync.Mutex
   432  	contract    common.Address              // peer's chequebook contract
   433  	beneficiary common.Address              // local peer's receiving address
   434  	sender      common.Address              // local peer's address to send cashing tx from
   435  	signer      *ecdsa.PublicKey            // peer's public key
   436  	txhash      string                      // tx hash of last cashing tx
   437  	session     *contract.ChequebookSession // abi contract backend with tx opts
   438  	quit        chan bool                   // when closed causes autocash to stop
   439  	maxUncashed *big.Int                    // threshold that triggers autocashing
   440  	cashed      *big.Int                    // cumulative amount cashed
   441  	cheque      *Cheque                     // last cheque, nil if none yet received
   442  	log         log.Logger                  // contextual logger with the contract address embedded
   443  }
   444  
   445  // NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated
   446  // from blockchain when first cheque is received.
   447  func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (*Inbox, error) {
   448  	if signer == nil {
   449  		return nil, fmt.Errorf("signer is null")
   450  	}
   451  	chbook, err := contract.NewChequebook(contractAddr, abigen)
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  	transactOpts := bind.NewKeyedTransactor(prvKey)
   456  	transactOpts.GasLimit = gasToCash
   457  	session := &contract.ChequebookSession{
   458  		Contract:     chbook,
   459  		TransactOpts: *transactOpts,
   460  	}
   461  	sender := transactOpts.From
   462  
   463  	inbox := &Inbox{
   464  		contract:    contractAddr,
   465  		beneficiary: beneficiary,
   466  		sender:      sender,
   467  		signer:      signer,
   468  		session:     session,
   469  		cashed:      new(big.Int).Set(common.Big0),
   470  		log:         log.New("contract", contractAddr),
   471  	}
   472  	inbox.log.Trace("New chequebook inbox initialized", "beneficiary", inbox.beneficiary, "signer", hexutil.Bytes(crypto.FromECDSAPub(signer)))
   473  	return inbox, nil
   474  }
   475  
   476  func (i *Inbox) String() string {
   477  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", i.contract.Hex(), i.beneficiary.Hex(), i.cheque.Amount)
   478  }
   479  
   480  // Stop quits the autocash goroutine.
   481  func (i *Inbox) Stop() {
   482  	defer i.lock.Unlock()
   483  	i.lock.Lock()
   484  	if i.quit != nil {
   485  		close(i.quit)
   486  		i.quit = nil
   487  	}
   488  }
   489  
   490  // Cash attempts to cash the current cheque.
   491  func (i *Inbox) Cash() (string, error) {
   492  	if i.cheque == nil {
   493  		return "", nil
   494  	}
   495  	txhash, err := i.cheque.Cash(i.session)
   496  	i.log.Trace("Cashing in chequebook cheque", "amount", i.cheque.Amount, "beneficiary", i.beneficiary)
   497  	i.cashed = i.cheque.Amount
   498  
   499  	return txhash, err
   500  }
   501  
   502  // AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed
   503  // cheque if maxUncashed is set to 0, then autocash on receipt.
   504  func (i *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) {
   505  	defer i.lock.Unlock()
   506  	i.lock.Lock()
   507  	i.maxUncashed = maxUncashed
   508  	i.autoCash(cashInterval)
   509  }
   510  
   511  // autoCash starts a loop that periodically clears the last cheque
   512  // if the peer is trusted. Clearing period could be 24h or a week.
   513  // The caller must hold lock.
   514  func (i *Inbox) autoCash(cashInterval time.Duration) {
   515  	if i.quit != nil {
   516  		close(i.quit)
   517  		i.quit = nil
   518  	}
   519  	// if maxUncashed is set to 0, then autocash on receipt
   520  	if cashInterval == time.Duration(0) || i.maxUncashed != nil && i.maxUncashed.Sign() == 0 {
   521  		return
   522  	}
   523  
   524  	ticker := time.NewTicker(cashInterval)
   525  	i.quit = make(chan bool)
   526  	quit := i.quit
   527  
   528  	go func() {
   529  		for {
   530  			select {
   531  			case <-quit:
   532  				return
   533  			case <-ticker.C:
   534  				i.lock.Lock()
   535  				if i.cheque != nil && i.cheque.Amount.Cmp(i.cashed) != 0 {
   536  					txhash, err := i.Cash()
   537  					if err == nil {
   538  						i.txhash = txhash
   539  					}
   540  				}
   541  				i.lock.Unlock()
   542  			}
   543  		}
   544  	}()
   545  }
   546  
   547  // Receive is called to deposit the latest cheque to the incoming Inbox.
   548  // The given promise must be a *Cheque.
   549  func (i *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
   550  	ch := promise.(*Cheque)
   551  
   552  	defer i.lock.Unlock()
   553  	i.lock.Lock()
   554  
   555  	var sum *big.Int
   556  	if i.cheque == nil {
   557  		// the sum is checked against the blockchain once a cheque is received
   558  		tally, err := i.session.Sent(i.beneficiary)
   559  		if err != nil {
   560  			return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err)
   561  		}
   562  		sum = tally
   563  	} else {
   564  		sum = i.cheque.Amount
   565  	}
   566  
   567  	amount, err := ch.Verify(i.signer, i.contract, i.beneficiary, sum)
   568  	var uncashed *big.Int
   569  	if err == nil {
   570  		i.cheque = ch
   571  
   572  		if i.maxUncashed != nil {
   573  			uncashed = new(big.Int).Sub(ch.Amount, i.cashed)
   574  			if i.maxUncashed.Cmp(uncashed) < 0 {
   575  				i.Cash()
   576  			}
   577  		}
   578  		i.log.Trace("Received cheque in chequebook inbox", "amount", amount, "uncashed", uncashed)
   579  	}
   580  
   581  	return amount, err
   582  }
   583  
   584  // Verify verifies cheque for signer, contract, beneficiary, amount, valid signature.
   585  func (ch *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
   586  	log.Trace("Verifying chequebook cheque", "cheque", ch, "sum", sum)
   587  	if sum == nil {
   588  		return nil, fmt.Errorf("invalid amount")
   589  	}
   590  
   591  	if ch.Beneficiary != beneficiary {
   592  		return nil, fmt.Errorf("beneficiary mismatch: %v != %v", ch.Beneficiary.Hex(), beneficiary.Hex())
   593  	}
   594  	if ch.Contract != contract {
   595  		return nil, fmt.Errorf("contract mismatch: %v != %v", ch.Contract.Hex(), contract.Hex())
   596  	}
   597  
   598  	amount := new(big.Int).Set(ch.Amount)
   599  	if sum != nil {
   600  		amount.Sub(amount, sum)
   601  		if amount.Sign() <= 0 {
   602  			return nil, fmt.Errorf("incorrect amount: %v <= 0", amount)
   603  		}
   604  	}
   605  
   606  	pubKey, err := crypto.SigToPub(sigHash(ch.Contract, beneficiary, ch.Amount), ch.Sig)
   607  	if err != nil {
   608  		return nil, fmt.Errorf("invalid signature: %v", err)
   609  	}
   610  	if !bytes.Equal(crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) {
   611  		return nil, fmt.Errorf("signer mismatch: %x != %x", crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey))
   612  	}
   613  	return amount, nil
   614  }
   615  
   616  // v/r/s representation of signature
   617  func sig2vrs(sig []byte) (v byte, r, s [32]byte) {
   618  	v = sig[64] + 27
   619  	copy(r[:], sig[:32])
   620  	copy(s[:], sig[32:64])
   621  	return
   622  }
   623  
   624  // Cash cashes the cheque by sending an Ethereum transaction.
   625  func (ch *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
   626  	v, r, s := sig2vrs(ch.Sig)
   627  	tx, err := session.Cash(ch.Beneficiary, ch.Amount, v, r, s)
   628  	if err != nil {
   629  		return "", err
   630  	}
   631  	return tx.Hash().Hex(), nil
   632  }
   633  
   634  // ValidateCode checks that the on-chain code at address matches the expected chequebook
   635  // contract code. This is used to detect suicided chequebooks.
   636  func ValidateCode(ctx context.Context, b Backend, address common.Address) (bool, error) {
   637  	code, err := b.CodeAt(ctx, address, nil)
   638  	if err != nil {
   639  		return false, err
   640  	}
   641  	return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil
   642  }