github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/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  	"crypto/ecdsa"
    30  	"encoding/json"
    31  	"fmt"
    32  	"io/ioutil"
    33  	"math/big"
    34  	"os"
    35  	"sync"
    36  	"time"
    37  
    38  	"github.com/atheioschain/go-atheios/accounts/abi/bind"
    39  	"github.com/atheioschain/go-atheios/common"
    40  	"github.com/atheioschain/go-atheios/contracts/chequebook/contract"
    41  	"github.com/atheioschain/go-atheios/core/types"
    42  	"github.com/atheioschain/go-atheios/crypto"
    43  	"github.com/atheioschain/go-atheios/logger"
    44  	"github.com/atheioschain/go-atheios/logger/glog"
    45  	"github.com/atheioschain/go-atheios/swarm/services/swap/swap"
    46  	"golang.org/x/net/context"
    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 require 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 beneficiarys
   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  
   110  func (self *Chequebook) String() string {
   111  	return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey)
   112  }
   113  
   114  // NewChequebook creates a new Chequebook.
   115  func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) {
   116  	balance := new(big.Int)
   117  	sent := make(map[common.Address]*big.Int)
   118  
   119  	chbook, err := contract.NewChequebook(contractAddr, backend)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	transactOpts := bind.NewKeyedTransactor(prvKey)
   124  	session := &contract.ChequebookSession{
   125  		Contract:     chbook,
   126  		TransactOpts: *transactOpts,
   127  	}
   128  
   129  	self = &Chequebook{
   130  		prvKey:       prvKey,
   131  		balance:      balance,
   132  		contractAddr: contractAddr,
   133  		sent:         sent,
   134  		path:         path,
   135  		backend:      backend,
   136  		owner:        transactOpts.From,
   137  		contract:     chbook,
   138  		session:      session,
   139  	}
   140  
   141  	if (contractAddr != common.Address{}) {
   142  		self.setBalanceFromBlockChain()
   143  		glog.V(logger.Detail).Infof("new chequebook initialised from %s (owner: %v, balance: %s)", contractAddr.Hex(), self.owner.Hex(), self.balance.String())
   144  	}
   145  	return
   146  }
   147  
   148  func (self *Chequebook) setBalanceFromBlockChain() {
   149  	balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil)
   150  	if err != nil {
   151  		glog.V(logger.Error).Infof("can't get balance: %v", err)
   152  	} else {
   153  		self.balance.Set(balance)
   154  	}
   155  }
   156  
   157  // LoadChequebook loads a chequebook from disk (file path).
   158  func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) {
   159  	var data []byte
   160  	data, err = ioutil.ReadFile(path)
   161  	if err != nil {
   162  		return
   163  	}
   164  
   165  	self, _ = NewChequebook(path, common.Address{}, prvKey, backend)
   166  
   167  	err = json.Unmarshal(data, self)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	if checkBalance {
   172  		self.setBalanceFromBlockChain()
   173  	}
   174  
   175  	glog.V(logger.Detail).Infof("loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contractAddr.Hex(), self.owner.Hex(), self.balance, path)
   176  
   177  	return
   178  }
   179  
   180  // chequebookFile is the JSON representation of a chequebook.
   181  type chequebookFile struct {
   182  	Balance  string
   183  	Contract string
   184  	Owner    string
   185  	Sent     map[string]string
   186  }
   187  
   188  // UnmarshalJSON deserialises a chequebook.
   189  func (self *Chequebook) UnmarshalJSON(data []byte) error {
   190  	var file chequebookFile
   191  	err := json.Unmarshal(data, &file)
   192  	if err != nil {
   193  		return err
   194  	}
   195  	_, ok := self.balance.SetString(file.Balance, 10)
   196  	if !ok {
   197  		return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance)
   198  	}
   199  	self.contractAddr = common.HexToAddress(file.Contract)
   200  	for addr, sent := range file.Sent {
   201  		self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
   202  		if !ok {
   203  			return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent)
   204  		}
   205  	}
   206  	return nil
   207  }
   208  
   209  // MarshalJSON serialises a chequebook.
   210  func (self *Chequebook) MarshalJSON() ([]byte, error) {
   211  	var file = &chequebookFile{
   212  		Balance:  self.balance.String(),
   213  		Contract: self.contractAddr.Hex(),
   214  		Owner:    self.owner.Hex(),
   215  		Sent:     make(map[string]string),
   216  	}
   217  	for addr, sent := range self.sent {
   218  		file.Sent[addr.Hex()] = sent.String()
   219  	}
   220  	return json.Marshal(file)
   221  }
   222  
   223  // Save persists the chequebook on disk, remembering balance, contract address and
   224  // cumulative amount of funds sent for each beneficiary.
   225  func (self *Chequebook) Save() (err error) {
   226  	data, err := json.MarshalIndent(self, "", " ")
   227  	if err != nil {
   228  		return err
   229  	}
   230  	glog.V(logger.Detail).Infof("saving chequebook (%s) to %v", self.contractAddr.Hex(), self.path)
   231  
   232  	return ioutil.WriteFile(self.path, data, os.ModePerm)
   233  }
   234  
   235  // Stop quits the autodeposit go routine to terminate
   236  func (self *Chequebook) Stop() {
   237  	defer self.lock.Unlock()
   238  	self.lock.Lock()
   239  	if self.quit != nil {
   240  		close(self.quit)
   241  		self.quit = nil
   242  	}
   243  }
   244  
   245  // Issue creates a cheque signed by the chequebook owner's private key. The
   246  // signer commits to a contract (one that they own), a beneficiary and amount.
   247  func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
   248  	defer self.lock.Unlock()
   249  	self.lock.Lock()
   250  
   251  	if amount.Cmp(common.Big0) <= 0 {
   252  		return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
   253  	}
   254  	if self.balance.Cmp(amount) < 0 {
   255  		err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
   256  	} else {
   257  		var sig []byte
   258  		sent, found := self.sent[beneficiary]
   259  		if !found {
   260  			sent = new(big.Int)
   261  			self.sent[beneficiary] = sent
   262  		}
   263  		sum := new(big.Int).Set(sent)
   264  		sum.Add(sum, amount)
   265  
   266  		sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey)
   267  		if err == nil {
   268  			ch = &Cheque{
   269  				Contract:    self.contractAddr,
   270  				Beneficiary: beneficiary,
   271  				Amount:      sum,
   272  				Sig:         sig,
   273  			}
   274  			sent.Set(sum)
   275  			self.balance.Sub(self.balance, amount) // subtract amount from balance
   276  		}
   277  	}
   278  
   279  	// auto deposit if threshold is set and balance is less then threshold
   280  	// note this is called even if issuing cheque fails
   281  	// so we reattempt depositing
   282  	if self.threshold != nil {
   283  		if self.balance.Cmp(self.threshold) < 0 {
   284  			send := new(big.Int).Sub(self.buffer, self.balance)
   285  			self.deposit(send)
   286  		}
   287  	}
   288  
   289  	return
   290  }
   291  
   292  // Cash is a convenience method to cash any cheque.
   293  func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
   294  	return ch.Cash(self.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 (self *Chequebook) Balance() *big.Int {
   312  	defer self.lock.Unlock()
   313  	self.lock.Lock()
   314  	return new(big.Int).Set(self.balance)
   315  }
   316  
   317  // Owner returns the owner account of the chequebook.
   318  func (self *Chequebook) Owner() common.Address {
   319  	return self.owner
   320  }
   321  
   322  // Address returns the on-chain contract address of the chequebook.
   323  func (self *Chequebook) Address() common.Address {
   324  	return self.contractAddr
   325  }
   326  
   327  // Deposit deposits money to the chequebook account.
   328  func (self *Chequebook) Deposit(amount *big.Int) (string, error) {
   329  	defer self.lock.Unlock()
   330  	self.lock.Lock()
   331  	return self.deposit(amount)
   332  }
   333  
   334  // deposit deposits amount to the chequebook account.
   335  // The caller must hold self.lock.
   336  func (self *Chequebook) deposit(amount *big.Int) (string, error) {
   337  	// since the amount is variable here, we do not use sessions
   338  	depositTransactor := bind.NewKeyedTransactor(self.prvKey)
   339  	depositTransactor.Value = amount
   340  	chbookRaw := &contract.ChequebookRaw{Contract: self.contract}
   341  	tx, err := chbookRaw.Transfer(depositTransactor)
   342  	if err != nil {
   343  		glog.V(logger.Warn).Infof("error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contractAddr.Hex(), self.balance, self.buffer, err)
   344  		return "", err
   345  	}
   346  	// assume that transaction is actually successful, we add the amount to balance right away
   347  	self.balance.Add(self.balance, amount)
   348  	glog.V(logger.Detail).Infof("deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contractAddr.Hex(), self.balance, self.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 (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   356  	defer self.lock.Unlock()
   357  	self.lock.Lock()
   358  	self.threshold = threshold
   359  	self.buffer = buffer
   360  	self.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 (self *Chequebook) autoDeposit(interval time.Duration) {
   366  	if self.quit != nil {
   367  		close(self.quit)
   368  		self.quit = nil
   369  	}
   370  	// if threshold >= balance autodeposit after every cheque issued
   371  	if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 {
   372  		return
   373  	}
   374  
   375  	ticker := time.NewTicker(interval)
   376  	self.quit = make(chan bool)
   377  	quit := self.quit
   378  	go func() {
   379  	FOR:
   380  		for {
   381  			select {
   382  			case <-quit:
   383  				break FOR
   384  			case <-ticker.C:
   385  				self.lock.Lock()
   386  				if self.balance.Cmp(self.buffer) < 0 {
   387  					amount := new(big.Int).Sub(self.buffer, self.balance)
   388  					txhash, err := self.deposit(amount)
   389  					if err == nil {
   390  						self.txhash = txhash
   391  					}
   392  				}
   393  				self.lock.Unlock()
   394  			}
   395  		}
   396  	}()
   397  	return
   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 (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
   413  	return self.chequeBook.Issue(self.beneficiary, amount)
   414  }
   415  
   416  // AutoDeposit enables auto-deposits on the underlying chequebook.
   417  func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
   418  	self.chequeBook.AutoDeposit(interval, threshold, buffer)
   419  }
   420  
   421  // Stop helps satisfy the swap.OutPayment interface.
   422  func (self *Outbox) Stop() {}
   423  
   424  // String implements fmt.Stringer.
   425  func (self *Outbox) String() string {
   426  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.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  	abigen      bind.ContractBackend        // blockchain API
   439  	session     *contract.ChequebookSession // abi contract backend with tx opts
   440  	quit        chan bool                   // when closed causes autocash to stop
   441  	maxUncashed *big.Int                    // threshold that triggers autocashing
   442  	cashed      *big.Int                    // cumulative amount cashed
   443  	cheque      *Cheque                     // last cheque, nil if none yet received
   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  	}
   472  	glog.V(logger.Detail).Infof("initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer))
   473  	return
   474  }
   475  
   476  func (self *Inbox) String() string {
   477  	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount)
   478  }
   479  
   480  // Stop quits the autocash goroutine.
   481  func (self *Inbox) Stop() {
   482  	defer self.lock.Unlock()
   483  	self.lock.Lock()
   484  	if self.quit != nil {
   485  		close(self.quit)
   486  		self.quit = nil
   487  	}
   488  }
   489  
   490  // Cash attempts to cash the current cheque.
   491  func (self *Inbox) Cash() (txhash string, err error) {
   492  	if self.cheque != nil {
   493  		txhash, err = self.cheque.Cash(self.session)
   494  		glog.V(logger.Detail).Infof("cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex())
   495  		self.cashed = self.cheque.Amount
   496  	}
   497  	return
   498  }
   499  
   500  // AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed
   501  // cheque if maxUncashed is set to 0, then autocash on receipt.
   502  func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) {
   503  	defer self.lock.Unlock()
   504  	self.lock.Lock()
   505  	self.maxUncashed = maxUncashed
   506  	self.autoCash(cashInterval)
   507  }
   508  
   509  // autoCash starts a loop that periodically clears the last check
   510  // if the peer is trusted. Clearing period could be 24h or a week.
   511  //
   512  // The caller must hold self.lock.
   513  func (self *Inbox) autoCash(cashInterval time.Duration) {
   514  	if self.quit != nil {
   515  		close(self.quit)
   516  		self.quit = nil
   517  	}
   518  	// if maxUncashed is set to 0, then autocash on receipt
   519  	if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Cmp(common.Big0) == 0 {
   520  		return
   521  	}
   522  
   523  	ticker := time.NewTicker(cashInterval)
   524  	self.quit = make(chan bool)
   525  	quit := self.quit
   526  	go func() {
   527  	FOR:
   528  		for {
   529  			select {
   530  			case <-quit:
   531  				break FOR
   532  			case <-ticker.C:
   533  				self.lock.Lock()
   534  				if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 {
   535  					txhash, err := self.Cash()
   536  					if err == nil {
   537  						self.txhash = txhash
   538  					}
   539  				}
   540  				self.lock.Unlock()
   541  			}
   542  		}
   543  	}()
   544  	return
   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 (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
   550  	ch := promise.(*Cheque)
   551  
   552  	defer self.lock.Unlock()
   553  	self.lock.Lock()
   554  
   555  	var sum *big.Int
   556  	if self.cheque == nil {
   557  		// the sum is checked against the blockchain once a check is received
   558  		tally, err := self.session.Sent(self.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 = self.cheque.Amount
   565  	}
   566  
   567  	amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum)
   568  	var uncashed *big.Int
   569  	if err == nil {
   570  		self.cheque = ch
   571  
   572  		if self.maxUncashed != nil {
   573  			uncashed = new(big.Int).Sub(ch.Amount, self.cashed)
   574  			if self.maxUncashed.Cmp(uncashed) < 0 {
   575  				self.Cash()
   576  			}
   577  		}
   578  		glog.V(logger.Detail).Infof("received cheque of %v wei in inbox (%s, uncashed: %v)", amount, self.contract.Hex(), uncashed)
   579  	}
   580  
   581  	return amount, err
   582  }
   583  
   584  // Verify verifies cheque for signer, contract, beneficiary, amount, valid signature.
   585  func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
   586  	glog.V(logger.Detail).Infof("verify cheque: %v - sum: %v", self, sum)
   587  	if sum == nil {
   588  		return nil, fmt.Errorf("invalid amount")
   589  	}
   590  
   591  	if self.Beneficiary != beneficiary {
   592  		return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex())
   593  	}
   594  	if self.Contract != contract {
   595  		return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex())
   596  	}
   597  
   598  	amount := new(big.Int).Set(self.Amount)
   599  	if sum != nil {
   600  		amount.Sub(amount, sum)
   601  		if amount.Cmp(common.Big0) <= 0 {
   602  			return nil, fmt.Errorf("incorrect amount: %v <= 0", amount)
   603  		}
   604  	}
   605  
   606  	pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.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 (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
   626  	v, r, s := sig2vrs(self.Sig)
   627  	tx, err := session.Cash(self.Beneficiary, self.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) (ok bool, err 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  }