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