github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/contracts/chequebook/cheque.go (about)

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