cheque.go 20 KB

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