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