| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | // Copyright 2016 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Package chequebook package wraps the 'chequebook' Ethereum smart contract. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The functions in this package allow using chequebook for | 
					
						
							|  |  |  | // issuing, receiving, verifying cheques in ether; (auto)cashing cheques in ether | 
					
						
							|  |  |  | // as well as (auto)depositing ether to the chequebook contract. | 
					
						
							|  |  |  | package chequebook | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 13:15:57 +01:00
										 |  |  | //go:generate abigen --sol contract/chequebook.sol --exc contract/mortal.sol:mortal,contract/owned.sol:owned --pkg contract --out contract/chequebook.go | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | //go:generate go run ./gencode.go | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2017-03-22 18:20:33 +01:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	"crypto/ecdsa" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/accounts/abi/bind" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2017-02-28 13:35:17 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common/hexutil" | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/contracts/chequebook/contract" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							| 
									
										
										
										
											2016-08-29 21:18:00 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/swarm/services/swap/swap" | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODO(zelig): watch peer solvency and notify of bouncing cheques | 
					
						
							|  |  |  | // TODO(zelig): enable paying with cheque by signing off | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | // Some functionality requires interacting with the blockchain: | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | // * setting current balance on peer's chequebook | 
					
						
							|  |  |  | // * sending the transaction to cash the cheque | 
					
						
							|  |  |  | // * depositing ether to the chequebook | 
					
						
							|  |  |  | // * watching incoming ether | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2017-11-13 13:47:27 +02:00
										 |  |  | 	gasToCash = uint64(2000000) // gas cost of a cash transaction using chequebook | 
					
						
							|  |  |  | 	// gasToDeploy = uint64(3000000) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Backend wraps all methods required for chequebook operation. | 
					
						
							|  |  |  | type Backend interface { | 
					
						
							|  |  |  | 	bind.ContractBackend | 
					
						
							|  |  |  | 	TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) | 
					
						
							|  |  |  | 	BalanceAt(ctx context.Context, address common.Address, blockNum *big.Int) (*big.Int, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cheque represents a payment promise to a single beneficiary. | 
					
						
							|  |  |  | type Cheque struct { | 
					
						
							|  |  |  | 	Contract    common.Address // address of chequebook, needed to avoid cross-contract submission | 
					
						
							|  |  |  | 	Beneficiary common.Address | 
					
						
							|  |  |  | 	Amount      *big.Int // cumulative amount of all funds sent | 
					
						
							|  |  |  | 	Sig         []byte   // signature Sign(Keccak256(contract, beneficiary, amount), prvKey) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (ch *Cheque) String() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", ch.Contract.Hex(), ch.Beneficiary.Hex(), ch.Amount, ch.Sig) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Params struct { | 
					
						
							|  |  |  | 	ContractCode, ContractAbi string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ContractParams = &Params{contract.ChequebookBin, contract.ChequebookABI} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Chequebook can create and sign cheques from a single contract to multiple beneficiaries. | 
					
						
							|  |  |  | // It is the outgoing payment handler for peer to peer micropayments. | 
					
						
							|  |  |  | type Chequebook struct { | 
					
						
							|  |  |  | 	path     string                      // path to chequebook file | 
					
						
							|  |  |  | 	prvKey   *ecdsa.PrivateKey           // private key to sign cheque with | 
					
						
							|  |  |  | 	lock     sync.Mutex                  // | 
					
						
							|  |  |  | 	backend  Backend                     // blockchain API | 
					
						
							|  |  |  | 	quit     chan bool                   // when closed causes autodeposit to stop | 
					
						
							|  |  |  | 	owner    common.Address              // owner address (derived from pubkey) | 
					
						
							|  |  |  | 	contract *contract.Chequebook        // abigen binding | 
					
						
							|  |  |  | 	session  *contract.ChequebookSession // abigen binding with Tx Opts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// persisted fields | 
					
						
							|  |  |  | 	balance      *big.Int                    // not synced with blockchain | 
					
						
							|  |  |  | 	contractAddr common.Address              // contract address | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | 	sent         map[common.Address]*big.Int //tallies for beneficiaries | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	txhash    string   // tx hash of last deposit tx | 
					
						
							|  |  |  | 	threshold *big.Int // threshold that triggers autodeposit if not nil | 
					
						
							|  |  |  | 	buffer    *big.Int // buffer to keep on top of balance for fork protection | 
					
						
							| 
									
										
										
										
											2017-02-28 13:35:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | 	log log.Logger // contextual logger with the contract address embedded | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) String() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", cb.contractAddr.Hex(), cb.owner.Hex(), cb.balance, cb.prvKey.PublicKey) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewChequebook creates a new Chequebook. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (*Chequebook, error) { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	balance := new(big.Int) | 
					
						
							|  |  |  | 	sent := make(map[common.Address]*big.Int) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	chbook, err := contract.NewChequebook(contractAddr, backend) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	transactOpts := bind.NewKeyedTransactor(prvKey) | 
					
						
							|  |  |  | 	session := &contract.ChequebookSession{ | 
					
						
							|  |  |  | 		Contract:     chbook, | 
					
						
							|  |  |  | 		TransactOpts: *transactOpts, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	cb := &Chequebook{ | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		prvKey:       prvKey, | 
					
						
							|  |  |  | 		balance:      balance, | 
					
						
							|  |  |  | 		contractAddr: contractAddr, | 
					
						
							|  |  |  | 		sent:         sent, | 
					
						
							|  |  |  | 		path:         path, | 
					
						
							|  |  |  | 		backend:      backend, | 
					
						
							|  |  |  | 		owner:        transactOpts.From, | 
					
						
							|  |  |  | 		contract:     chbook, | 
					
						
							|  |  |  | 		session:      session, | 
					
						
							| 
									
										
										
										
											2017-02-28 13:35:17 +02:00
										 |  |  | 		log:          log.New("contract", contractAddr), | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (contractAddr != common.Address{}) { | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		cb.setBalanceFromBlockChain() | 
					
						
							|  |  |  | 		cb.log.Trace("New chequebook initialised", "owner", cb.owner, "balance", cb.balance) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	return cb, nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) setBalanceFromBlockChain() { | 
					
						
							|  |  |  | 	balance, err := cb.backend.BalanceAt(context.TODO(), cb.contractAddr, nil) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-02-28 13:35:17 +02:00
										 |  |  | 		log.Error("Failed to retrieve chequebook balance", "err", err) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		cb.balance.Set(balance) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LoadChequebook loads a chequebook from disk (file path). | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (*Chequebook, error) { | 
					
						
							|  |  |  | 	data, err := ioutil.ReadFile(path) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	cb, _ := NewChequebook(path, common.Address{}, prvKey, backend) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	if err = json.Unmarshal(data, cb); err != nil { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if checkBalance { | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		cb.setBalanceFromBlockChain() | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-28 13:35:17 +02:00
										 |  |  | 	log.Trace("Loaded chequebook from disk", "path", path) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	return cb, nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // chequebookFile is the JSON representation of a chequebook. | 
					
						
							|  |  |  | type chequebookFile struct { | 
					
						
							|  |  |  | 	Balance  string | 
					
						
							|  |  |  | 	Contract string | 
					
						
							|  |  |  | 	Owner    string | 
					
						
							|  |  |  | 	Sent     map[string]string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalJSON deserialises a chequebook. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) UnmarshalJSON(data []byte) error { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	var file chequebookFile | 
					
						
							|  |  |  | 	err := json.Unmarshal(data, &file) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	_, ok := cb.balance.SetString(file.Balance, 10) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	cb.contractAddr = common.HexToAddress(file.Contract) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	for addr, sent := range file.Sent { | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		cb.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MarshalJSON serialises a chequebook. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) MarshalJSON() ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	var file = &chequebookFile{ | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		Balance:  cb.balance.String(), | 
					
						
							|  |  |  | 		Contract: cb.contractAddr.Hex(), | 
					
						
							|  |  |  | 		Owner:    cb.owner.Hex(), | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		Sent:     make(map[string]string), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	for addr, sent := range cb.sent { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		file.Sent[addr.Hex()] = sent.String() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return json.Marshal(file) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Save persists the chequebook on disk, remembering balance, contract address and | 
					
						
							|  |  |  | // cumulative amount of funds sent for each beneficiary. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Save() error { | 
					
						
							|  |  |  | 	data, err := json.MarshalIndent(cb, "", " ") | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	cb.log.Trace("Saving chequebook to disk", cb.path) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	return ioutil.WriteFile(cb.path, data, os.ModePerm) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stop quits the autodeposit go routine to terminate | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Stop() { | 
					
						
							|  |  |  | 	defer cb.lock.Unlock() | 
					
						
							|  |  |  | 	cb.lock.Lock() | 
					
						
							|  |  |  | 	if cb.quit != nil { | 
					
						
							|  |  |  | 		close(cb.quit) | 
					
						
							|  |  |  | 		cb.quit = nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Issue creates a cheque signed by the chequebook owner's private key. The | 
					
						
							|  |  |  | // signer commits to a contract (one that they own), a beneficiary and amount. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (*Cheque, error) { | 
					
						
							|  |  |  | 	defer cb.lock.Unlock() | 
					
						
							|  |  |  | 	cb.lock.Lock() | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 15:09:11 +01:00
										 |  |  | 	if amount.Sign() <= 0 { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		return nil, fmt.Errorf("amount must be greater than zero (%v)", amount) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		ch  *Cheque | 
					
						
							|  |  |  | 		err error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if cb.balance.Cmp(amount) < 0 { | 
					
						
							|  |  |  | 		err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, cb.balance) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		var sig []byte | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		sent, found := cb.sent[beneficiary] | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		if !found { | 
					
						
							|  |  |  | 			sent = new(big.Int) | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 			cb.sent[beneficiary] = sent | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		sum := new(big.Int).Set(sent) | 
					
						
							|  |  |  | 		sum.Add(sum, amount) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		sig, err = crypto.Sign(sigHash(cb.contractAddr, beneficiary, sum), cb.prvKey) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			ch = &Cheque{ | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 				Contract:    cb.contractAddr, | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 				Beneficiary: beneficiary, | 
					
						
							|  |  |  | 				Amount:      sum, | 
					
						
							|  |  |  | 				Sig:         sig, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			sent.Set(sum) | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 			cb.balance.Sub(cb.balance, amount) // subtract amount from balance | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// auto deposit if threshold is set and balance is less then threshold | 
					
						
							| 
									
										
										
										
											2017-01-06 19:44:35 +02:00
										 |  |  | 	// note this is called even if issuing cheque fails | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	// so we reattempt depositing | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	if cb.threshold != nil { | 
					
						
							|  |  |  | 		if cb.balance.Cmp(cb.threshold) < 0 { | 
					
						
							|  |  |  | 			send := new(big.Int).Sub(cb.buffer, cb.balance) | 
					
						
							|  |  |  | 			cb.deposit(send) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	return ch, err | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cash is a convenience method to cash any cheque. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Cash(ch *Cheque) (string, error) { | 
					
						
							|  |  |  | 	return ch.Cash(cb.session) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // data to sign: contract address, beneficiary, cumulative amount of funds ever sent | 
					
						
							|  |  |  | func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte { | 
					
						
							|  |  |  | 	bigamount := sum.Bytes() | 
					
						
							|  |  |  | 	if len(bigamount) > 32 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var amount32 [32]byte | 
					
						
							|  |  |  | 	copy(amount32[32-len(bigamount):32], bigamount) | 
					
						
							|  |  |  | 	input := append(contract.Bytes(), beneficiary.Bytes()...) | 
					
						
							|  |  |  | 	input = append(input, amount32[:]...) | 
					
						
							|  |  |  | 	return crypto.Keccak256(input) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Balance returns the current balance of the chequebook. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Balance() *big.Int { | 
					
						
							|  |  |  | 	defer cb.lock.Unlock() | 
					
						
							|  |  |  | 	cb.lock.Lock() | 
					
						
							|  |  |  | 	return new(big.Int).Set(cb.balance) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Owner returns the owner account of the chequebook. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Owner() common.Address { | 
					
						
							|  |  |  | 	return cb.owner | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Address returns the on-chain contract address of the chequebook. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Address() common.Address { | 
					
						
							|  |  |  | 	return cb.contractAddr | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Deposit deposits money to the chequebook account. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) Deposit(amount *big.Int) (string, error) { | 
					
						
							|  |  |  | 	defer cb.lock.Unlock() | 
					
						
							|  |  |  | 	cb.lock.Lock() | 
					
						
							|  |  |  | 	return cb.deposit(amount) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // deposit deposits amount to the chequebook account. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | // The caller must hold lock. | 
					
						
							|  |  |  | func (cb *Chequebook) deposit(amount *big.Int) (string, error) { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	// since the amount is variable here, we do not use sessions | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	depositTransactor := bind.NewKeyedTransactor(cb.prvKey) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	depositTransactor.Value = amount | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	chbookRaw := &contract.ChequebookRaw{Contract: cb.contract} | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	tx, err := chbookRaw.Transfer(depositTransactor) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 		cb.log.Warn("Failed to fund chequebook", "amount", amount, "balance", cb.balance, "target", cb.buffer, "err", err) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// assume that transaction is actually successful, we add the amount to balance right away | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	cb.balance.Add(cb.balance, amount) | 
					
						
							|  |  |  | 	cb.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", cb.balance, "target", cb.buffer) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	return tx.Hash().Hex(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AutoDeposit (re)sets interval time and amount which triggers sending funds to the | 
					
						
							|  |  |  | // chequebook. Contract backend needs to be set if threshold is not less than buffer, then | 
					
						
							|  |  |  | // deposit will be triggered on every new cheque issued. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { | 
					
						
							|  |  |  | 	defer cb.lock.Unlock() | 
					
						
							|  |  |  | 	cb.lock.Lock() | 
					
						
							|  |  |  | 	cb.threshold = threshold | 
					
						
							|  |  |  | 	cb.buffer = buffer | 
					
						
							|  |  |  | 	cb.autoDeposit(interval) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // autoDeposit starts a goroutine that periodically sends funds to the chequebook | 
					
						
							|  |  |  | // contract caller holds the lock the go routine terminates if Chequebook.quit is closed. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (cb *Chequebook) autoDeposit(interval time.Duration) { | 
					
						
							|  |  |  | 	if cb.quit != nil { | 
					
						
							|  |  |  | 		close(cb.quit) | 
					
						
							|  |  |  | 		cb.quit = nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// if threshold >= balance autodeposit after every cheque issued | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	if interval == time.Duration(0) || cb.threshold != nil && cb.buffer != nil && cb.threshold.Cmp(cb.buffer) >= 0 { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ticker := time.NewTicker(interval) | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	cb.quit = make(chan bool) | 
					
						
							|  |  |  | 	quit := cb.quit | 
					
						
							| 
									
										
										
										
											2017-08-08 19:41:35 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-quit: | 
					
						
							| 
									
										
										
										
											2017-08-08 19:41:35 +03:00
										 |  |  | 				return | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 			case <-ticker.C: | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 				cb.lock.Lock() | 
					
						
							|  |  |  | 				if cb.balance.Cmp(cb.buffer) < 0 { | 
					
						
							|  |  |  | 					amount := new(big.Int).Sub(cb.buffer, cb.balance) | 
					
						
							|  |  |  | 					txhash, err := cb.deposit(amount) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 					if err == nil { | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 						cb.txhash = txhash | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 				cb.lock.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Outbox can issue cheques from a single contract to a single beneficiary. | 
					
						
							|  |  |  | type Outbox struct { | 
					
						
							|  |  |  | 	chequeBook  *Chequebook | 
					
						
							|  |  |  | 	beneficiary common.Address | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewOutbox creates an outbox. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func NewOutbox(cb *Chequebook, beneficiary common.Address) *Outbox { | 
					
						
							|  |  |  | 	return &Outbox{cb, beneficiary} | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Issue creates cheque. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (o *Outbox) Issue(amount *big.Int) (swap.Promise, error) { | 
					
						
							|  |  |  | 	return o.chequeBook.Issue(o.beneficiary, amount) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AutoDeposit enables auto-deposits on the underlying chequebook. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (o *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { | 
					
						
							|  |  |  | 	o.chequeBook.AutoDeposit(interval, threshold, buffer) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stop helps satisfy the swap.OutPayment interface. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (o *Outbox) Stop() {} | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // String implements fmt.Stringer. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (o *Outbox) String() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", o.chequeBook.Address().Hex(), o.beneficiary.Hex(), o.chequeBook.Balance()) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Inbox can deposit, verify and cash cheques from a single contract to a single | 
					
						
							|  |  |  | // beneficiary. It is the incoming payment handler for peer to peer micropayments. | 
					
						
							|  |  |  | type Inbox struct { | 
					
						
							|  |  |  | 	lock        sync.Mutex | 
					
						
							|  |  |  | 	contract    common.Address              // peer's chequebook contract | 
					
						
							|  |  |  | 	beneficiary common.Address              // local peer's receiving address | 
					
						
							|  |  |  | 	sender      common.Address              // local peer's address to send cashing tx from | 
					
						
							|  |  |  | 	signer      *ecdsa.PublicKey            // peer's public key | 
					
						
							|  |  |  | 	txhash      string                      // tx hash of last cashing tx | 
					
						
							|  |  |  | 	session     *contract.ChequebookSession // abi contract backend with tx opts | 
					
						
							|  |  |  | 	quit        chan bool                   // when closed causes autocash to stop | 
					
						
							|  |  |  | 	maxUncashed *big.Int                    // threshold that triggers autocashing | 
					
						
							|  |  |  | 	cashed      *big.Int                    // cumulative amount cashed | 
					
						
							|  |  |  | 	cheque      *Cheque                     // last cheque, nil if none yet received | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | 	log         log.Logger                  // contextual logger with the contract address embedded | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated | 
					
						
							|  |  |  | // from blockchain when first cheque is received. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (*Inbox, error) { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if signer == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("signer is null") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	chbook, err := contract.NewChequebook(contractAddr, abigen) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	transactOpts := bind.NewKeyedTransactor(prvKey) | 
					
						
							|  |  |  | 	transactOpts.GasLimit = gasToCash | 
					
						
							|  |  |  | 	session := &contract.ChequebookSession{ | 
					
						
							|  |  |  | 		Contract:     chbook, | 
					
						
							|  |  |  | 		TransactOpts: *transactOpts, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sender := transactOpts.From | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	inbox := &Inbox{ | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		contract:    contractAddr, | 
					
						
							|  |  |  | 		beneficiary: beneficiary, | 
					
						
							|  |  |  | 		sender:      sender, | 
					
						
							|  |  |  | 		signer:      signer, | 
					
						
							|  |  |  | 		session:     session, | 
					
						
							|  |  |  | 		cashed:      new(big.Int).Set(common.Big0), | 
					
						
							| 
									
										
										
										
											2017-02-28 13:35:17 +02:00
										 |  |  | 		log:         log.New("contract", contractAddr), | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	inbox.log.Trace("New chequebook inbox initialized", "beneficiary", inbox.beneficiary, "signer", hexutil.Bytes(crypto.FromECDSAPub(signer))) | 
					
						
							|  |  |  | 	return inbox, nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (i *Inbox) String() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", i.contract.Hex(), i.beneficiary.Hex(), i.cheque.Amount) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stop quits the autocash goroutine. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (i *Inbox) Stop() { | 
					
						
							|  |  |  | 	defer i.lock.Unlock() | 
					
						
							|  |  |  | 	i.lock.Lock() | 
					
						
							|  |  |  | 	if i.quit != nil { | 
					
						
							|  |  |  | 		close(i.quit) | 
					
						
							|  |  |  | 		i.quit = nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cash attempts to cash the current cheque. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func (i *Inbox) Cash() (string, error) { | 
					
						
							|  |  |  | 	if i.cheque == nil { | 
					
						
							|  |  |  | 		return "", nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | 	txhash, err := i.cheque.Cash(i.session) | 
					
						
							|  |  |  | 	i.log.Trace("Cashing in chequebook cheque", "amount", i.cheque.Amount, "beneficiary", i.beneficiary) | 
					
						
							|  |  |  | 	i.cashed = i.cheque.Amount | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return txhash, err | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed | 
					
						
							|  |  |  | // cheque if maxUncashed is set to 0, then autocash on receipt. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (i *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) { | 
					
						
							|  |  |  | 	defer i.lock.Unlock() | 
					
						
							|  |  |  | 	i.lock.Lock() | 
					
						
							|  |  |  | 	i.maxUncashed = maxUncashed | 
					
						
							|  |  |  | 	i.autoCash(cashInterval) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | // autoCash starts a loop that periodically clears the last cheque | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | // if the peer is trusted. Clearing period could be 24h or a week. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | // The caller must hold lock. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (i *Inbox) autoCash(cashInterval time.Duration) { | 
					
						
							|  |  |  | 	if i.quit != nil { | 
					
						
							|  |  |  | 		close(i.quit) | 
					
						
							|  |  |  | 		i.quit = nil | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// if maxUncashed is set to 0, then autocash on receipt | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	if cashInterval == time.Duration(0) || i.maxUncashed != nil && i.maxUncashed.Sign() == 0 { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ticker := time.NewTicker(cashInterval) | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	i.quit = make(chan bool) | 
					
						
							|  |  |  | 	quit := i.quit | 
					
						
							| 
									
										
										
										
											2017-08-08 19:41:35 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-quit: | 
					
						
							| 
									
										
										
										
											2017-08-08 19:41:35 +03:00
										 |  |  | 				return | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 			case <-ticker.C: | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 				i.lock.Lock() | 
					
						
							|  |  |  | 				if i.cheque != nil && i.cheque.Amount.Cmp(i.cashed) != 0 { | 
					
						
							|  |  |  | 					txhash, err := i.Cash() | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 					if err == nil { | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 						i.txhash = txhash | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 				i.lock.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Receive is called to deposit the latest cheque to the incoming Inbox. | 
					
						
							|  |  |  | // The given promise must be a *Cheque. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (i *Inbox) Receive(promise swap.Promise) (*big.Int, error) { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	ch := promise.(*Cheque) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	defer i.lock.Unlock() | 
					
						
							|  |  |  | 	i.lock.Lock() | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var sum *big.Int | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	if i.cheque == nil { | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | 		// the sum is checked against the blockchain once a cheque is received | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 		tally, err := i.session.Sent(i.beneficiary) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | 			return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		sum = tally | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 		sum = i.cheque.Amount | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	amount, err := ch.Verify(i.signer, i.contract, i.beneficiary, sum) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	var uncashed *big.Int | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 		i.cheque = ch | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 		if i.maxUncashed != nil { | 
					
						
							|  |  |  | 			uncashed = new(big.Int).Sub(ch.Amount, i.cashed) | 
					
						
							|  |  |  | 			if i.maxUncashed.Cmp(uncashed) < 0 { | 
					
						
							|  |  |  | 				i.Cash() | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 		i.log.Trace("Received cheque in chequebook inbox", "amount", amount, "uncashed", uncashed) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return amount, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Verify verifies cheque for signer, contract, beneficiary, amount, valid signature. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (ch *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) { | 
					
						
							|  |  |  | 	log.Trace("Verifying chequebook cheque", "cheque", ch, "sum", sum) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if sum == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid amount") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	if ch.Beneficiary != beneficiary { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("beneficiary mismatch: %v != %v", ch.Beneficiary.Hex(), beneficiary.Hex()) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	if ch.Contract != contract { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("contract mismatch: %v != %v", ch.Contract.Hex(), contract.Hex()) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	amount := new(big.Int).Set(ch.Amount) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if sum != nil { | 
					
						
							|  |  |  | 		amount.Sub(amount, sum) | 
					
						
							| 
									
										
										
										
											2017-02-28 15:09:11 +01:00
										 |  |  | 		if amount.Sign() <= 0 { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 			return nil, fmt.Errorf("incorrect amount: %v <= 0", amount) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | 	pubKey, err := crypto.SigToPub(sigHash(ch.Contract, beneficiary, ch.Amount), ch.Sig) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid signature: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !bytes.Equal(crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("signer mismatch: %x != %x", crypto.FromECDSAPub(pubKey), crypto.FromECDSAPub(signerKey)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return amount, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // v/r/s representation of signature | 
					
						
							|  |  |  | func sig2vrs(sig []byte) (v byte, r, s [32]byte) { | 
					
						
							|  |  |  | 	v = sig[64] + 27 | 
					
						
							|  |  |  | 	copy(r[:], sig[:32]) | 
					
						
							|  |  |  | 	copy(s[:], sig[32:64]) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cash cashes the cheque by sending an Ethereum transaction. | 
					
						
							| 
									
										
										
										
											2018-04-30 16:05:24 -07:00
										 |  |  | func (ch *Cheque) Cash(session *contract.ChequebookSession) (string, error) { | 
					
						
							|  |  |  | 	v, r, s := sig2vrs(ch.Sig) | 
					
						
							|  |  |  | 	tx, err := session.Cash(ch.Beneficiary, ch.Amount, v, r, s) | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tx.Hash().Hex(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ValidateCode checks that the on-chain code at address matches the expected chequebook | 
					
						
							|  |  |  | // contract code. This is used to detect suicided chequebooks. | 
					
						
							| 
									
										
										
										
											2019-02-07 13:14:24 +02:00
										 |  |  | func ValidateCode(ctx context.Context, b Backend, address common.Address) (bool, error) { | 
					
						
							| 
									
										
										
										
											2016-08-28 13:34:59 +02:00
										 |  |  | 	code, err := b.CodeAt(ctx, address, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil | 
					
						
							|  |  |  | } |