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