mirror of https://github.com/ethereum/go-ethereum
The wrapper code is adapted from the swarm/services/chequebook package with the following mostly cosmetic changes: * The code now uses the new Go API interfaces to query balances. Some minor functional changes were required to make this work. * The package no longer depends on swarm/services/swap/swap. References to swap.Promise are replaced by interface{}, the base type of Promise. This is temporary. * The contract wrapper has been regenerated with latest abigen and solc v0.3.6. * There is a new generator that creates the 'deployed code' variable. * Documentation comments now follow the recommended godoc style. * [CHEQUEBOOK] log prefixes are gone. * LGPL license headers have been added to all files.pull/2956/head
parent
5fc032a9d1
commit
6c8b023298
@ -0,0 +1,68 @@ |
|||||||
|
// 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 |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
) |
||||||
|
|
||||||
|
const Version = "1.0" |
||||||
|
|
||||||
|
var errNoChequebook = errors.New("no chequebook") |
||||||
|
|
||||||
|
type Api struct { |
||||||
|
chequebookf func() *Chequebook |
||||||
|
} |
||||||
|
|
||||||
|
func NewApi(ch func() *Chequebook) *Api { |
||||||
|
return &Api{ch} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Api) Balance() (string, error) { |
||||||
|
ch := self.chequebookf() |
||||||
|
if ch == nil { |
||||||
|
return "", errNoChequebook |
||||||
|
} |
||||||
|
return ch.Balance().String(), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) { |
||||||
|
ch := self.chequebookf() |
||||||
|
if ch == nil { |
||||||
|
return nil, errNoChequebook |
||||||
|
} |
||||||
|
return ch.Issue(beneficiary, amount) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Api) Cash(cheque *Cheque) (txhash string, err error) { |
||||||
|
ch := self.chequebookf() |
||||||
|
if ch == nil { |
||||||
|
return "", errNoChequebook |
||||||
|
} |
||||||
|
return ch.Cash(cheque) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Api) Deposit(amount *big.Int) (txhash string, err error) { |
||||||
|
ch := self.chequebookf() |
||||||
|
if ch == nil { |
||||||
|
return "", errNoChequebook |
||||||
|
} |
||||||
|
return ch.Deposit(amount) |
||||||
|
} |
@ -0,0 +1,643 @@ |
|||||||
|
// 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 |
||||||
|
|
||||||
|
//go:generate abigen --sol contract/chequebook.sol --pkg contract --out contract/chequebook.go
|
||||||
|
//go:generate go run ./gencode.go
|
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"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" |
||||||
|
"github.com/ethereum/go-ethereum/contracts/chequebook/contract" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/logger" |
||||||
|
"github.com/ethereum/go-ethereum/logger/glog" |
||||||
|
"golang.org/x/net/context" |
||||||
|
) |
||||||
|
|
||||||
|
// TODO(zelig): watch peer solvency and notify of bouncing cheques
|
||||||
|
// TODO(zelig): enable paying with cheque by signing off
|
||||||
|
|
||||||
|
// Some functionality require interacting with the blockchain:
|
||||||
|
// * setting current balance on peer's chequebook
|
||||||
|
// * sending the transaction to cash the cheque
|
||||||
|
// * depositing ether to the chequebook
|
||||||
|
// * watching incoming ether
|
||||||
|
|
||||||
|
var ( |
||||||
|
gasToCash = big.NewInt(2000000) // gas cost of a cash transaction using chequebook
|
||||||
|
// gasToDeploy = big.NewInt(3000000)
|
||||||
|
) |
||||||
|
|
||||||
|
// 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)
|
||||||
|
} |
||||||
|
|
||||||
|
func (self *Cheque) String() string { |
||||||
|
return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig) |
||||||
|
} |
||||||
|
|
||||||
|
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
|
||||||
|
sent map[common.Address]*big.Int //tallies for beneficiarys
|
||||||
|
|
||||||
|
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
|
||||||
|
} |
||||||
|
|
||||||
|
func (self *Chequebook) String() string { |
||||||
|
return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey) |
||||||
|
} |
||||||
|
|
||||||
|
// NewChequebook creates a new Chequebook.
|
||||||
|
func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) { |
||||||
|
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, |
||||||
|
} |
||||||
|
|
||||||
|
self = &Chequebook{ |
||||||
|
prvKey: prvKey, |
||||||
|
balance: balance, |
||||||
|
contractAddr: contractAddr, |
||||||
|
sent: sent, |
||||||
|
path: path, |
||||||
|
backend: backend, |
||||||
|
owner: transactOpts.From, |
||||||
|
contract: chbook, |
||||||
|
session: session, |
||||||
|
} |
||||||
|
|
||||||
|
if (contractAddr != common.Address{}) { |
||||||
|
self.setBalanceFromBlockChain() |
||||||
|
glog.V(logger.Detail).Infof("new chequebook initialised from %s (owner: %v, balance: %s)", contractAddr.Hex(), self.owner.Hex(), self.balance.String()) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Chequebook) setBalanceFromBlockChain() { |
||||||
|
balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil) |
||||||
|
if err != nil { |
||||||
|
glog.V(logger.Error).Infof("can't get balance: %v", err) |
||||||
|
} else { |
||||||
|
self.balance.Set(balance) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// LoadChequebook loads a chequebook from disk (file path).
|
||||||
|
func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) { |
||||||
|
var data []byte |
||||||
|
data, err = ioutil.ReadFile(path) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
self, _ = NewChequebook(path, common.Address{}, prvKey, backend) |
||||||
|
|
||||||
|
err = json.Unmarshal(data, self) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
if checkBalance { |
||||||
|
self.setBalanceFromBlockChain() |
||||||
|
} |
||||||
|
|
||||||
|
glog.V(logger.Detail).Infof("loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contractAddr.Hex(), self.owner.Hex(), self.balance, path) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (self *Chequebook) UnmarshalJSON(data []byte) error { |
||||||
|
var file chequebookFile |
||||||
|
err := json.Unmarshal(data, &file) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
_, ok := self.balance.SetString(file.Balance, 10) |
||||||
|
if !ok { |
||||||
|
return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance) |
||||||
|
} |
||||||
|
self.contractAddr = common.HexToAddress(file.Contract) |
||||||
|
for addr, sent := range file.Sent { |
||||||
|
self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10) |
||||||
|
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.
|
||||||
|
func (self *Chequebook) MarshalJSON() ([]byte, error) { |
||||||
|
var file = &chequebookFile{ |
||||||
|
Balance: self.balance.String(), |
||||||
|
Contract: self.contractAddr.Hex(), |
||||||
|
Owner: self.owner.Hex(), |
||||||
|
Sent: make(map[string]string), |
||||||
|
} |
||||||
|
for addr, sent := range self.sent { |
||||||
|
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.
|
||||||
|
func (self *Chequebook) Save() (err error) { |
||||||
|
data, err := json.MarshalIndent(self, "", " ") |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
glog.V(logger.Detail).Infof("saving chequebook (%s) to %v", self.contractAddr.Hex(), self.path) |
||||||
|
|
||||||
|
return ioutil.WriteFile(self.path, data, os.ModePerm) |
||||||
|
} |
||||||
|
|
||||||
|
// Stop quits the autodeposit go routine to terminate
|
||||||
|
func (self *Chequebook) Stop() { |
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
if self.quit != nil { |
||||||
|
close(self.quit) |
||||||
|
self.quit = nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) { |
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
|
||||||
|
if amount.Cmp(common.Big0) <= 0 { |
||||||
|
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount) |
||||||
|
} |
||||||
|
if self.balance.Cmp(amount) < 0 { |
||||||
|
err = fmt.Errorf("insufficent funds to issue cheque for amount: %v. balance: %v", amount, self.balance) |
||||||
|
} else { |
||||||
|
var sig []byte |
||||||
|
sent, found := self.sent[beneficiary] |
||||||
|
if !found { |
||||||
|
sent = new(big.Int) |
||||||
|
self.sent[beneficiary] = sent |
||||||
|
} |
||||||
|
sum := new(big.Int).Set(sent) |
||||||
|
sum.Add(sum, amount) |
||||||
|
|
||||||
|
sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey) |
||||||
|
if err == nil { |
||||||
|
ch = &Cheque{ |
||||||
|
Contract: self.contractAddr, |
||||||
|
Beneficiary: beneficiary, |
||||||
|
Amount: sum, |
||||||
|
Sig: sig, |
||||||
|
} |
||||||
|
sent.Set(sum) |
||||||
|
self.balance.Sub(self.balance, amount) // subtract amount from balance
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// auto deposit if threshold is set and balance is less then threshold
|
||||||
|
// note this is called even if issueing cheque fails
|
||||||
|
// so we reattempt depositing
|
||||||
|
if self.threshold != nil { |
||||||
|
if self.balance.Cmp(self.threshold) < 0 { |
||||||
|
send := new(big.Int).Sub(self.buffer, self.balance) |
||||||
|
self.deposit(send) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Cash is a convenience method to cash any cheque.
|
||||||
|
func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) { |
||||||
|
return ch.Cash(self.session) |
||||||
|
} |
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (self *Chequebook) Balance() *big.Int { |
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
return new(big.Int).Set(self.balance) |
||||||
|
} |
||||||
|
|
||||||
|
// Owner returns the owner account of the chequebook.
|
||||||
|
func (self *Chequebook) Owner() common.Address { |
||||||
|
return self.owner |
||||||
|
} |
||||||
|
|
||||||
|
// Address returns the on-chain contract address of the chequebook.
|
||||||
|
func (self *Chequebook) Address() common.Address { |
||||||
|
return self.contractAddr |
||||||
|
} |
||||||
|
|
||||||
|
// Deposit deposits money to the chequebook account.
|
||||||
|
func (self *Chequebook) Deposit(amount *big.Int) (string, error) { |
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
return self.deposit(amount) |
||||||
|
} |
||||||
|
|
||||||
|
// deposit deposits amount to the chequebook account.
|
||||||
|
// The caller must hold self.lock.
|
||||||
|
func (self *Chequebook) deposit(amount *big.Int) (string, error) { |
||||||
|
// since the amount is variable here, we do not use sessions
|
||||||
|
depositTransactor := bind.NewKeyedTransactor(self.prvKey) |
||||||
|
depositTransactor.Value = amount |
||||||
|
chbookRaw := &contract.ChequebookRaw{Contract: self.contract} |
||||||
|
tx, err := chbookRaw.Transfer(depositTransactor) |
||||||
|
if err != nil { |
||||||
|
glog.V(logger.Warn).Infof("error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contractAddr.Hex(), self.balance, self.buffer, err) |
||||||
|
return "", err |
||||||
|
} |
||||||
|
// assume that transaction is actually successful, we add the amount to balance right away
|
||||||
|
self.balance.Add(self.balance, amount) |
||||||
|
glog.V(logger.Detail).Infof("deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contractAddr.Hex(), self.balance, self.buffer) |
||||||
|
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.
|
||||||
|
func (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { |
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
self.threshold = threshold |
||||||
|
self.buffer = buffer |
||||||
|
self.autoDeposit(interval) |
||||||
|
} |
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (self *Chequebook) autoDeposit(interval time.Duration) { |
||||||
|
if self.quit != nil { |
||||||
|
close(self.quit) |
||||||
|
self.quit = nil |
||||||
|
} |
||||||
|
// if threshold >= balance autodeposit after every cheque issued
|
||||||
|
if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
ticker := time.NewTicker(interval) |
||||||
|
self.quit = make(chan bool) |
||||||
|
quit := self.quit |
||||||
|
go func() { |
||||||
|
FOR: |
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-quit: |
||||||
|
break FOR |
||||||
|
case <-ticker.C: |
||||||
|
self.lock.Lock() |
||||||
|
if self.balance.Cmp(self.buffer) < 0 { |
||||||
|
amount := new(big.Int).Sub(self.buffer, self.balance) |
||||||
|
txhash, err := self.deposit(amount) |
||||||
|
if err == nil { |
||||||
|
self.txhash = txhash |
||||||
|
} |
||||||
|
} |
||||||
|
self.lock.Unlock() |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Outbox can issue cheques from a single contract to a single beneficiary.
|
||||||
|
type Outbox struct { |
||||||
|
chequeBook *Chequebook |
||||||
|
beneficiary common.Address |
||||||
|
} |
||||||
|
|
||||||
|
// NewOutbox creates an outbox.
|
||||||
|
func NewOutbox(chbook *Chequebook, beneficiary common.Address) *Outbox { |
||||||
|
return &Outbox{chbook, beneficiary} |
||||||
|
} |
||||||
|
|
||||||
|
// Issue creates cheque.
|
||||||
|
func (self *Outbox) Issue(amount *big.Int) (interface{}, error) { |
||||||
|
// TODO(fjl): the return type should be more descriptive.
|
||||||
|
return self.chequeBook.Issue(self.beneficiary, amount) |
||||||
|
} |
||||||
|
|
||||||
|
// AutoDeposit enables auto-deposits on the underlying chequebook.
|
||||||
|
func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) { |
||||||
|
self.chequeBook.AutoDeposit(interval, threshold, buffer) |
||||||
|
} |
||||||
|
|
||||||
|
// Stop helps satisfy the swap.OutPayment interface.
|
||||||
|
func (self *Outbox) Stop() {} |
||||||
|
|
||||||
|
// String implements fmt.Stringer.
|
||||||
|
func (self *Outbox) String() string { |
||||||
|
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance()) |
||||||
|
} |
||||||
|
|
||||||
|
// 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
|
||||||
|
abigen bind.ContractBackend // blockchain API
|
||||||
|
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
|
||||||
|
} |
||||||
|
|
||||||
|
// NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated
|
||||||
|
// from blockchain when first cheque is received.
|
||||||
|
func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address, signer *ecdsa.PublicKey, abigen bind.ContractBackend) (self *Inbox, err error) { |
||||||
|
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 |
||||||
|
|
||||||
|
self = &Inbox{ |
||||||
|
contract: contractAddr, |
||||||
|
beneficiary: beneficiary, |
||||||
|
sender: sender, |
||||||
|
signer: signer, |
||||||
|
session: session, |
||||||
|
cashed: new(big.Int).Set(common.Big0), |
||||||
|
} |
||||||
|
glog.V(logger.Detail).Infof("initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer)) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Inbox) String() string { |
||||||
|
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount) |
||||||
|
} |
||||||
|
|
||||||
|
// Stop quits the autocash goroutine.
|
||||||
|
func (self *Inbox) Stop() { |
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
if self.quit != nil { |
||||||
|
close(self.quit) |
||||||
|
self.quit = nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Cash attempts to cash the current cheque.
|
||||||
|
func (self *Inbox) Cash() (txhash string, err error) { |
||||||
|
if self.cheque != nil { |
||||||
|
txhash, err = self.cheque.Cash(self.session) |
||||||
|
glog.V(logger.Detail).Infof("cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex()) |
||||||
|
self.cashed = self.cheque.Amount |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) { |
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
self.maxUncashed = maxUncashed |
||||||
|
self.autoCash(cashInterval) |
||||||
|
} |
||||||
|
|
||||||
|
// autoCash starts a loop that periodically clears the last check
|
||||||
|
// if the peer is trusted. Clearing period could be 24h or a week.
|
||||||
|
//
|
||||||
|
// The caller must hold self.lock.
|
||||||
|
func (self *Inbox) autoCash(cashInterval time.Duration) { |
||||||
|
if self.quit != nil { |
||||||
|
close(self.quit) |
||||||
|
self.quit = nil |
||||||
|
} |
||||||
|
// if maxUncashed is set to 0, then autocash on receipt
|
||||||
|
if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Cmp(common.Big0) == 0 { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
ticker := time.NewTicker(cashInterval) |
||||||
|
self.quit = make(chan bool) |
||||||
|
quit := self.quit |
||||||
|
go func() { |
||||||
|
FOR: |
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-quit: |
||||||
|
break FOR |
||||||
|
case <-ticker.C: |
||||||
|
self.lock.Lock() |
||||||
|
if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 { |
||||||
|
txhash, err := self.Cash() |
||||||
|
if err == nil { |
||||||
|
self.txhash = txhash |
||||||
|
} |
||||||
|
} |
||||||
|
self.lock.Unlock() |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Receive is called to deposit the latest cheque to the incoming Inbox.
|
||||||
|
// The given promise must be a *Cheque.
|
||||||
|
func (self *Inbox) Receive(promise interface{}) (*big.Int, error) { |
||||||
|
// TODO(fjl): the type of promise should be safer
|
||||||
|
ch := promise.(*Cheque) |
||||||
|
|
||||||
|
defer self.lock.Unlock() |
||||||
|
self.lock.Lock() |
||||||
|
|
||||||
|
var sum *big.Int |
||||||
|
if self.cheque == nil { |
||||||
|
// the sum is checked against the blockchain once a check is received
|
||||||
|
tally, err := self.session.Sent(self.beneficiary) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err) |
||||||
|
} |
||||||
|
sum = tally |
||||||
|
} else { |
||||||
|
sum = self.cheque.Amount |
||||||
|
} |
||||||
|
|
||||||
|
amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum) |
||||||
|
var uncashed *big.Int |
||||||
|
if err == nil { |
||||||
|
self.cheque = ch |
||||||
|
|
||||||
|
if self.maxUncashed != nil { |
||||||
|
uncashed = new(big.Int).Sub(ch.Amount, self.cashed) |
||||||
|
if self.maxUncashed.Cmp(uncashed) < 0 { |
||||||
|
self.Cash() |
||||||
|
} |
||||||
|
} |
||||||
|
glog.V(logger.Detail).Infof("received cheque of %v wei in inbox (%s, uncashed: %v)", amount, self.contract.Hex(), uncashed) |
||||||
|
} |
||||||
|
|
||||||
|
return amount, err |
||||||
|
} |
||||||
|
|
||||||
|
// Verify verifies cheque for signer, contract, beneficiary, amount, valid signature.
|
||||||
|
func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) { |
||||||
|
glog.V(logger.Detail).Infof("verify cheque: %v - sum: %v", self, sum) |
||||||
|
if sum == nil { |
||||||
|
return nil, fmt.Errorf("invalid amount") |
||||||
|
} |
||||||
|
|
||||||
|
if self.Beneficiary != beneficiary { |
||||||
|
return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex()) |
||||||
|
} |
||||||
|
if self.Contract != contract { |
||||||
|
return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex()) |
||||||
|
} |
||||||
|
|
||||||
|
amount := new(big.Int).Set(self.Amount) |
||||||
|
if sum != nil { |
||||||
|
amount.Sub(amount, sum) |
||||||
|
if amount.Cmp(common.Big0) <= 0 { |
||||||
|
return nil, fmt.Errorf("incorrect amount: %v <= 0", amount) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.Sig) |
||||||
|
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.
|
||||||
|
func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) { |
||||||
|
v, r, s := sig2vrs(self.Sig) |
||||||
|
tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s) |
||||||
|
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.
|
||||||
|
func ValidateCode(ctx context.Context, b Backend, address common.Address) (ok bool, err error) { |
||||||
|
code, err := b.CodeAt(ctx, address, nil) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
return bytes.Equal(code, common.FromHex(contract.ContractDeployedCode)), nil |
||||||
|
} |
@ -0,0 +1,528 @@ |
|||||||
|
// 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 |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/ecdsa" |
||||||
|
"math/big" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind" |
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/contracts/chequebook/contract" |
||||||
|
"github.com/ethereum/go-ethereum/core" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
key0, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") |
||||||
|
key1, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") |
||||||
|
key2, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") |
||||||
|
addr0 = crypto.PubkeyToAddress(key0.PublicKey) |
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey) |
||||||
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey) |
||||||
|
) |
||||||
|
|
||||||
|
func newTestBackend() *backends.SimulatedBackend { |
||||||
|
return backends.NewSimulatedBackend( |
||||||
|
core.GenesisAccount{Address: addr0, Balance: big.NewInt(1000000000)}, |
||||||
|
core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000000)}, |
||||||
|
core.GenesisAccount{Address: addr2, Balance: big.NewInt(1000000000)}, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) { |
||||||
|
deployTransactor := bind.NewKeyedTransactor(prvKey) |
||||||
|
deployTransactor.Value = amount |
||||||
|
addr, _, _, err := contract.DeployChequebook(deployTransactor, backend) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, err |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
return addr, nil |
||||||
|
} |
||||||
|
|
||||||
|
func TestIssueAndReceive(t *testing.T) { |
||||||
|
path := filepath.Join(os.TempDir(), "chequebook-test.json") |
||||||
|
backend := newTestBackend() |
||||||
|
addr0, err := deploy(key0, big.NewInt(0), backend) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("deploy contract: expected no error, got %v", err) |
||||||
|
} |
||||||
|
chbook, err := NewChequebook(path, addr0, key0, backend) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
chbook.sent[addr1] = new(big.Int).SetUint64(42) |
||||||
|
amount := common.Big1 |
||||||
|
ch, err := chbook.Issue(addr1, amount) |
||||||
|
if err == nil { |
||||||
|
t.Fatalf("expected insufficient funds error, got none") |
||||||
|
} |
||||||
|
|
||||||
|
chbook.balance = new(big.Int).Set(common.Big1) |
||||||
|
if chbook.Balance().Cmp(common.Big1) != 0 { |
||||||
|
t.Fatalf("expected: %v, got %v", "0", chbook.Balance()) |
||||||
|
} |
||||||
|
|
||||||
|
ch, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
if chbook.Balance().Cmp(common.Big0) != 0 { |
||||||
|
t.Errorf("expected: %v, got %v", "0", chbook.Balance()) |
||||||
|
} |
||||||
|
|
||||||
|
chbox, err := NewInbox(key1, addr0, addr1, &key0.PublicKey, backend) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
received, err := chbox.Receive(ch) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
if received.Cmp(big.NewInt(43)) != 0 { |
||||||
|
t.Errorf("expected: %v, got %v", "43", received) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func TestCheckbookFile(t *testing.T) { |
||||||
|
path := filepath.Join(os.TempDir(), "chequebook-test.json") |
||||||
|
backend := newTestBackend() |
||||||
|
chbook, err := NewChequebook(path, addr0, key0, backend) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
chbook.sent[addr1] = new(big.Int).SetUint64(42) |
||||||
|
chbook.balance = new(big.Int).Set(common.Big1) |
||||||
|
|
||||||
|
chbook.Save() |
||||||
|
|
||||||
|
chbook, err = LoadChequebook(path, key0, backend, false) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
if chbook.Balance().Cmp(common.Big1) != 0 { |
||||||
|
t.Errorf("expected: %v, got %v", "0", chbook.Balance()) |
||||||
|
} |
||||||
|
|
||||||
|
ch, err := chbook.Issue(addr1, common.Big1) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
if ch.Amount.Cmp(new(big.Int).SetUint64(43)) != 0 { |
||||||
|
t.Errorf("expected: %v, got %v", "0", ch.Amount) |
||||||
|
} |
||||||
|
|
||||||
|
err = chbook.Save() |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestVerifyErrors(t *testing.T) { |
||||||
|
path0 := filepath.Join(os.TempDir(), "chequebook-test-0.json") |
||||||
|
backend := newTestBackend() |
||||||
|
contr0, err := deploy(key0, common.Big2, backend) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
chbook0, err := NewChequebook(path0, contr0, key0, backend) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
path1 := filepath.Join(os.TempDir(), "chequebook-test-1.json") |
||||||
|
contr1, err := deploy(key1, common.Big2, backend) |
||||||
|
chbook1, err := NewChequebook(path1, contr1, key1, backend) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
chbook0.sent[addr1] = new(big.Int).SetUint64(42) |
||||||
|
chbook0.balance = new(big.Int).Set(common.Big2) |
||||||
|
chbook1.balance = new(big.Int).Set(common.Big1) |
||||||
|
amount := common.Big1 |
||||||
|
ch0, err := chbook0.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
time.Sleep(5) |
||||||
|
chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
received, err := chbox.Receive(ch0) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
if received.Cmp(big.NewInt(43)) != 0 { |
||||||
|
t.Errorf("expected: %v, got %v", "43", received) |
||||||
|
} |
||||||
|
|
||||||
|
ch1, err := chbook0.Issue(addr2, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
received, err = chbox.Receive(ch1) |
||||||
|
t.Logf("correct error: %v", err) |
||||||
|
if err == nil { |
||||||
|
t.Fatalf("expected receiver error, got none") |
||||||
|
} |
||||||
|
|
||||||
|
ch2, err := chbook1.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
received, err = chbox.Receive(ch2) |
||||||
|
t.Logf("correct error: %v", err) |
||||||
|
if err == nil { |
||||||
|
t.Fatalf("expected sender error, got none") |
||||||
|
} |
||||||
|
|
||||||
|
_, err = chbook1.Issue(addr1, new(big.Int).SetInt64(-1)) |
||||||
|
t.Logf("correct error: %v", err) |
||||||
|
if err == nil { |
||||||
|
t.Fatalf("expected incorrect amount error, got none") |
||||||
|
} |
||||||
|
|
||||||
|
received, err = chbox.Receive(ch0) |
||||||
|
t.Logf("correct error: %v", err) |
||||||
|
if err == nil { |
||||||
|
t.Fatalf("expected incorrect amount error, got none") |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func TestDeposit(t *testing.T) { |
||||||
|
path0 := filepath.Join(os.TempDir(), "chequebook-test-0.json") |
||||||
|
backend := newTestBackend() |
||||||
|
contr0, err := deploy(key0, new(big.Int), backend) |
||||||
|
chbook, err := NewChequebook(path0, contr0, key0, backend) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
balance := new(big.Int).SetUint64(42) |
||||||
|
chbook.Deposit(balance) |
||||||
|
backend.Commit() |
||||||
|
if chbook.balance.Cmp(balance) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
amount := common.Big1 |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
exp := new(big.Int).SetUint64(41) |
||||||
|
if chbook.balance.Cmp(exp) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
// autodeposit on each issue
|
||||||
|
chbook.AutoDeposit(0, balance, balance) |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
if chbook.balance.Cmp(balance) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
// autodeposit off
|
||||||
|
chbook.AutoDeposit(0, common.Big0, balance) |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
exp = new(big.Int).SetUint64(40) |
||||||
|
if chbook.balance.Cmp(exp) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
// autodeposit every 10ms if new cheque issued
|
||||||
|
interval := 30 * time.Millisecond |
||||||
|
chbook.AutoDeposit(interval, common.Big1, balance) |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
exp = new(big.Int).SetUint64(38) |
||||||
|
if chbook.balance.Cmp(exp) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
time.Sleep(3 * interval) |
||||||
|
backend.Commit() |
||||||
|
if chbook.balance.Cmp(balance) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
exp = new(big.Int).SetUint64(40) |
||||||
|
chbook.AutoDeposit(4*interval, exp, balance) |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
time.Sleep(3 * interval) |
||||||
|
backend.Commit() |
||||||
|
if chbook.balance.Cmp(exp) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
_, err = chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
time.Sleep(1 * interval) |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
if chbook.balance.Cmp(balance) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", balance, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
chbook.AutoDeposit(1*interval, common.Big0, balance) |
||||||
|
chbook.Stop() |
||||||
|
|
||||||
|
_, err = chbook.Issue(addr1, common.Big1) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
_, err = chbook.Issue(addr1, common.Big2) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
time.Sleep(1 * interval) |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
exp = new(big.Int).SetUint64(39) |
||||||
|
if chbook.balance.Cmp(exp) != 0 { |
||||||
|
t.Fatalf("expected balance %v, got %v", exp, chbook.balance) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func TestCash(t *testing.T) { |
||||||
|
path := filepath.Join(os.TempDir(), "chequebook-test.json") |
||||||
|
backend := newTestBackend() |
||||||
|
contr0, err := deploy(key0, common.Big2, backend) |
||||||
|
chbook, err := NewChequebook(path, contr0, key0, backend) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
chbook.sent[addr1] = new(big.Int).SetUint64(42) |
||||||
|
amount := common.Big1 |
||||||
|
chbook.balance = new(big.Int).Set(common.Big1) |
||||||
|
ch, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
chbox, err := NewInbox(key1, contr0, addr1, &key0.PublicKey, backend) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
// cashing latest cheque
|
||||||
|
_, err = chbox.Receive(ch) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
_, err = ch.Cash(chbook.session) |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
chbook.balance = new(big.Int).Set(common.Big3) |
||||||
|
ch0, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
ch1, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
interval := 10 * time.Millisecond |
||||||
|
// setting autocash with interval of 10ms
|
||||||
|
chbox.AutoCash(interval, nil) |
||||||
|
_, err = chbox.Receive(ch0) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
_, err = chbox.Receive(ch1) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
// expBalance := big.NewInt(2)
|
||||||
|
// gotBalance := backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
// after 3x interval time and 2 cheques received, exactly one cashing tx is sent
|
||||||
|
time.Sleep(4 * interval) |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
// expBalance = big.NewInt(4)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// after stopping autocash no more tx are sent
|
||||||
|
ch2, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
chbox.Stop() |
||||||
|
_, err = chbox.Receive(ch2) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
time.Sleep(2 * interval) |
||||||
|
backend.Commit() |
||||||
|
// expBalance = big.NewInt(4)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// autocash below 1
|
||||||
|
chbook.balance = big.NewInt(2) |
||||||
|
chbox.AutoCash(0, common.Big1) |
||||||
|
|
||||||
|
ch3, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
// expBalance = big.NewInt(4)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
ch4, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
_, err = chbox.Receive(ch3) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
_, err = chbox.Receive(ch4) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
|
||||||
|
// 2 checks of amount 1 received, exactly 1 tx is sent
|
||||||
|
// expBalance = big.NewInt(6)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// autochash on receipt when maxUncashed is 0
|
||||||
|
chbook.balance = new(big.Int).Set(common.Big2) |
||||||
|
chbox.AutoCash(0, common.Big0) |
||||||
|
|
||||||
|
ch5, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
// expBalance = big.NewInt(5)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
ch6, err := chbook.Issue(addr1, amount) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
_, err = chbox.Receive(ch5) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
// expBalance = big.NewInt(4)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
_, err = chbox.Receive(ch6) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("expected no error, got %v", err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
// expBalance = big.NewInt(6)
|
||||||
|
// gotBalance = backend.BalanceAt(addr1)
|
||||||
|
// if gotBalance.Cmp(expBalance) != 0 {
|
||||||
|
// t.Fatalf("expected beneficiary balance %v, got %v", expBalance, gotBalance)
|
||||||
|
// }
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,554 @@ |
|||||||
|
// 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 contract |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi" |
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
) |
||||||
|
|
||||||
|
// ChequebookABI is the input ABI used to generate the binding from.
|
||||||
|
const ChequebookABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"sent","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"},{"name":"sig_v","type":"uint8"},{"name":"sig_r","type":"bytes32"},{"name":"sig_s","type":"bytes32"}],"name":"cash","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"deadbeat","type":"address"}],"name":"Overdraft","type":"event"}]` |
||||||
|
|
||||||
|
// ChequebookBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const ChequebookBin = `0x606060405260008054600160a060020a03191633179055610223806100246000396000f3606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100b957600054600160a060020a0316ff5b6100a760043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bb576101cf565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a03908116911614610156576101cf565b600160a060020a038681166000908152600160205260409020543090911631908603106101d75760406000818120549151600160a060020a0389169288039082818181858883f19350505050156101cf57846001600050600088600160a060020a03168152602001908152602001600020600050819055505b505050505050565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff` |
||||||
|
|
||||||
|
// DeployChequebook deploys a new Ethereum contract, binding an instance of Chequebook to it.
|
||||||
|
func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Chequebook, error) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ChequebookBin), backend) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Chequebook is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Chequebook struct { |
||||||
|
ChequebookCaller // Read-only binding to the contract
|
||||||
|
ChequebookTransactor // Write-only binding to the contract
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookCaller struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookTransactor struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type ChequebookSession struct { |
||||||
|
Contract *Chequebook // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type ChequebookCallerSession struct { |
||||||
|
Contract *ChequebookCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type ChequebookTransactorSession struct { |
||||||
|
Contract *ChequebookTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type ChequebookRaw struct { |
||||||
|
Contract *Chequebook // Generic contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookCallerRaw struct { |
||||||
|
Contract *ChequebookCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// ChequebookTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type ChequebookTransactorRaw struct { |
||||||
|
Contract *ChequebookTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// NewChequebook creates a new instance of Chequebook, bound to a specific deployed contract.
|
||||||
|
func NewChequebook(address common.Address, backend bind.ContractBackend) (*Chequebook, error) { |
||||||
|
contract, err := bindChequebook(address, backend, backend) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewChequebookCaller creates a new read-only instance of Chequebook, bound to a specific deployed contract.
|
||||||
|
func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*ChequebookCaller, error) { |
||||||
|
contract, err := bindChequebook(address, caller, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &ChequebookCaller{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewChequebookTransactor creates a new write-only instance of Chequebook, bound to a specific deployed contract.
|
||||||
|
func NewChequebookTransactor(address common.Address, transactor bind.ContractTransactor) (*ChequebookTransactor, error) { |
||||||
|
contract, err := bindChequebook(address, nil, transactor) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &ChequebookTransactor{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// bindChequebook binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Chequebook *ChequebookRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _Chequebook.Contract.ChequebookCaller.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Chequebook *ChequebookRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.ChequebookTransactor.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Chequebook *ChequebookRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.ChequebookTransactor.contract.Transact(opts, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Chequebook *ChequebookCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _Chequebook.Contract.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Chequebook *ChequebookTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Chequebook *ChequebookTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.contract.Transact(opts, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Sent is a free data retrieval call binding the contract method 0x7bf786f8.
|
||||||
|
//
|
||||||
|
// Solidity: function sent( address) constant returns(uint256)
|
||||||
|
func (_Chequebook *ChequebookCaller) Sent(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { |
||||||
|
var ( |
||||||
|
ret0 = new(*big.Int) |
||||||
|
) |
||||||
|
out := ret0 |
||||||
|
err := _Chequebook.contract.Call(opts, out, "sent", arg0) |
||||||
|
return *ret0, err |
||||||
|
} |
||||||
|
|
||||||
|
// Sent is a free data retrieval call binding the contract method 0x7bf786f8.
|
||||||
|
//
|
||||||
|
// Solidity: function sent( address) constant returns(uint256)
|
||||||
|
func (_Chequebook *ChequebookSession) Sent(arg0 common.Address) (*big.Int, error) { |
||||||
|
return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) |
||||||
|
} |
||||||
|
|
||||||
|
// Sent is a free data retrieval call binding the contract method 0x7bf786f8.
|
||||||
|
//
|
||||||
|
// Solidity: function sent( address) constant returns(uint256)
|
||||||
|
func (_Chequebook *ChequebookCallerSession) Sent(arg0 common.Address) (*big.Int, error) { |
||||||
|
return _Chequebook.Contract.Sent(&_Chequebook.CallOpts, arg0) |
||||||
|
} |
||||||
|
|
||||||
|
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||||
|
//
|
||||||
|
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||||
|
func (_Chequebook *ChequebookTransactor) Cash(opts *bind.TransactOpts, beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { |
||||||
|
return _Chequebook.contract.Transact(opts, "cash", beneficiary, amount, sig_v, sig_r, sig_s) |
||||||
|
} |
||||||
|
|
||||||
|
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||||
|
//
|
||||||
|
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||||
|
func (_Chequebook *ChequebookSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) |
||||||
|
} |
||||||
|
|
||||||
|
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||||
|
//
|
||||||
|
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||||
|
func (_Chequebook *ChequebookTransactorSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s) |
||||||
|
} |
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Chequebook *ChequebookTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Chequebook.contract.Transact(opts, "kill") |
||||||
|
} |
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Chequebook *ChequebookSession) Kill() (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) |
||||||
|
} |
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) { |
||||||
|
return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) |
||||||
|
} |
||||||
|
|
||||||
|
// MortalABI is the input ABI used to generate the binding from.
|
||||||
|
const MortalABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"type":"function"}]` |
||||||
|
|
||||||
|
// MortalBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const MortalBin = `0x606060405260008054600160a060020a03191633179055605c8060226000396000f3606060405260e060020a600035046341c0e1b58114601a575b005b60186000543373ffffffffffffffffffffffffffffffffffffffff90811691161415605a5760005473ffffffffffffffffffffffffffffffffffffffff16ff5b56` |
||||||
|
|
||||||
|
// DeployMortal deploys a new Ethereum contract, binding an instance of Mortal to it.
|
||||||
|
func DeployMortal(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Mortal, error) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(MortalABI)) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(MortalBin), backend) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
return address, tx, &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Mortal is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Mortal struct { |
||||||
|
MortalCaller // Read-only binding to the contract
|
||||||
|
MortalTransactor // Write-only binding to the contract
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type MortalCaller struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type MortalTransactor struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type MortalSession struct { |
||||||
|
Contract *Mortal // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type MortalCallerSession struct { |
||||||
|
Contract *MortalCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type MortalTransactorSession struct { |
||||||
|
Contract *MortalTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type MortalRaw struct { |
||||||
|
Contract *Mortal // Generic contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type MortalCallerRaw struct { |
||||||
|
Contract *MortalCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// MortalTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type MortalTransactorRaw struct { |
||||||
|
Contract *MortalTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// NewMortal creates a new instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortal(address common.Address, backend bind.ContractBackend) (*Mortal, error) { |
||||||
|
contract, err := bindMortal(address, backend, backend) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &Mortal{MortalCaller: MortalCaller{contract: contract}, MortalTransactor: MortalTransactor{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewMortalCaller creates a new read-only instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortalCaller(address common.Address, caller bind.ContractCaller) (*MortalCaller, error) { |
||||||
|
contract, err := bindMortal(address, caller, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &MortalCaller{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewMortalTransactor creates a new write-only instance of Mortal, bound to a specific deployed contract.
|
||||||
|
func NewMortalTransactor(address common.Address, transactor bind.ContractTransactor) (*MortalTransactor, error) { |
||||||
|
contract, err := bindMortal(address, nil, transactor) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &MortalTransactor{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// bindMortal binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindMortal(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(MortalABI)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Mortal *MortalRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _Mortal.Contract.MortalCaller.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Mortal *MortalRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Mortal.Contract.MortalTransactor.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Mortal *MortalRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _Mortal.Contract.MortalTransactor.contract.Transact(opts, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Mortal *MortalCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _Mortal.Contract.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Mortal *MortalTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Mortal.Contract.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Mortal *MortalTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _Mortal.Contract.contract.Transact(opts, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalTransactor) Kill(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Mortal.contract.Transact(opts, "kill") |
||||||
|
} |
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalSession) Kill() (*types.Transaction, error) { |
||||||
|
return _Mortal.Contract.Kill(&_Mortal.TransactOpts) |
||||||
|
} |
||||||
|
|
||||||
|
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||||
|
//
|
||||||
|
// Solidity: function kill() returns()
|
||||||
|
func (_Mortal *MortalTransactorSession) Kill() (*types.Transaction, error) { |
||||||
|
return _Mortal.Contract.Kill(&_Mortal.TransactOpts) |
||||||
|
} |
||||||
|
|
||||||
|
// OwnedABI is the input ABI used to generate the binding from.
|
||||||
|
const OwnedABI = `[{"inputs":[],"type":"constructor"}]` |
||||||
|
|
||||||
|
// OwnedBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const OwnedBin = `0x606060405260008054600160a060020a0319163317905560068060226000396000f3606060405200` |
||||||
|
|
||||||
|
// DeployOwned deploys a new Ethereum contract, binding an instance of Owned to it.
|
||||||
|
func DeployOwned(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Owned, error) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(OwnedABI)) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(OwnedBin), backend) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, nil, nil, err |
||||||
|
} |
||||||
|
return address, tx, &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Owned is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type Owned struct { |
||||||
|
OwnedCaller // Read-only binding to the contract
|
||||||
|
OwnedTransactor // Write-only binding to the contract
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedCaller struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedTransactor struct { |
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type OwnedSession struct { |
||||||
|
Contract *Owned // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type OwnedCallerSession struct { |
||||||
|
Contract *OwnedCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type OwnedTransactorSession struct { |
||||||
|
Contract *OwnedTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type OwnedRaw struct { |
||||||
|
Contract *Owned // Generic contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedCallerRaw struct { |
||||||
|
Contract *OwnedCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// OwnedTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type OwnedTransactorRaw struct { |
||||||
|
Contract *OwnedTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
} |
||||||
|
|
||||||
|
// NewOwned creates a new instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwned(address common.Address, backend bind.ContractBackend) (*Owned, error) { |
||||||
|
contract, err := bindOwned(address, backend, backend) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &Owned{OwnedCaller: OwnedCaller{contract: contract}, OwnedTransactor: OwnedTransactor{contract: contract}}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewOwnedCaller creates a new read-only instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwnedCaller(address common.Address, caller bind.ContractCaller) (*OwnedCaller, error) { |
||||||
|
contract, err := bindOwned(address, caller, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &OwnedCaller{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewOwnedTransactor creates a new write-only instance of Owned, bound to a specific deployed contract.
|
||||||
|
func NewOwnedTransactor(address common.Address, transactor bind.ContractTransactor) (*OwnedTransactor, error) { |
||||||
|
contract, err := bindOwned(address, nil, transactor) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &OwnedTransactor{contract: contract}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// bindOwned binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindOwned(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { |
||||||
|
parsed, err := abi.JSON(strings.NewReader(OwnedABI)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Owned *OwnedRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _Owned.Contract.OwnedCaller.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Owned *OwnedRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Owned.Contract.OwnedTransactor.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Owned *OwnedRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _Owned.Contract.OwnedTransactor.contract.Transact(opts, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_Owned *OwnedCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||||
|
return _Owned.Contract.contract.Call(opts, result, method, params...) |
||||||
|
} |
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_Owned *OwnedTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||||
|
return _Owned.Contract.contract.Transfer(opts) |
||||||
|
} |
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_Owned *OwnedTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||||
|
return _Owned.Contract.contract.Transact(opts, method, params...) |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
import "mortal"; |
||||||
|
|
||||||
|
/// @title Chequebook for Ethereum micropayments |
||||||
|
/// @author Daniel A. Nagy <daniel@ethdev.com> |
||||||
|
contract chequebook is mortal { |
||||||
|
// Cumulative paid amount in wei to each beneficiary |
||||||
|
mapping (address => uint256) public sent; |
||||||
|
|
||||||
|
/// @notice Overdraft event |
||||||
|
event Overdraft(address deadbeat); |
||||||
|
|
||||||
|
/// @notice Cash cheque |
||||||
|
/// |
||||||
|
/// @param beneficiary beneficiary address |
||||||
|
/// @param amount cumulative amount in wei |
||||||
|
/// @param sig_v signature parameter v |
||||||
|
/// @param sig_r signature parameter r |
||||||
|
/// @param sig_s signature parameter s |
||||||
|
/// The digital signature is calculated on the concatenated triplet of contract address, beneficiary address and cumulative amount |
||||||
|
function cash(address beneficiary, uint256 amount, |
||||||
|
uint8 sig_v, bytes32 sig_r, bytes32 sig_s) { |
||||||
|
// Check if the cheque is old. |
||||||
|
// Only cheques that are more recent than the last cashed one are considered. |
||||||
|
if(amount <= sent[beneficiary]) return; |
||||||
|
// Check the digital signature of the cheque. |
||||||
|
bytes32 hash = sha3(address(this), beneficiary, amount); |
||||||
|
if(owner != ecrecover(hash, sig_v, sig_r, sig_s)) return; |
||||||
|
// Attempt sending the difference between the cumulative amount on the cheque |
||||||
|
// and the cumulative amount on the last cashed cheque to beneficiary. |
||||||
|
if (amount - sent[beneficiary] >= this.balance) { |
||||||
|
if (beneficiary.send(amount - sent[beneficiary])) { |
||||||
|
// Upon success, update the cumulative amount. |
||||||
|
sent[beneficiary] = amount; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Upon failure, punish owner for writing a bounced cheque. |
||||||
|
// owner.sendToDebtorsPrison(); |
||||||
|
Overdraft(owner); |
||||||
|
// Compensate beneficiary. |
||||||
|
suicide(beneficiary); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
// 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 contract |
||||||
|
|
||||||
|
// ContractDeployedCode is used to detect suicides. This constant needs to be
|
||||||
|
// updated when the contract code is changed.
|
||||||
|
const ContractDeployedCode = "0x606060405260e060020a600035046341c0e1b581146100315780637bf786f814610059578063fbf788d614610071575b005b61002f60005433600160a060020a03908116911614156100b957600054600160a060020a0316ff5b6100a760043560016020526000908152604090205481565b61002f600435602435604435606435608435600160a060020a03851660009081526001602052604081205485116100bb576101cf565b60408051918252519081900360200190f35b565b50604080516c0100000000000000000000000030600160a060020a0390811682028352881602601482015260288101869052815190819003604801812080825260ff861660208381019190915282840186905260608301859052925190926001926080818101939182900301816000866161da5a03f11561000257505060405151600054600160a060020a03908116911614610156576101cf565b600160a060020a038681166000908152600160205260409020543090911631908603106101d75760406000818120549151600160a060020a0389169288039082818181858883f19350505050156101cf57846001600050600088600160a060020a03168152602001908152602001600020600050819055505b505050505050565b60005460408051600160a060020a03929092168252517f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f9789181900360200190a185600160a060020a0316ff" |
@ -0,0 +1,73 @@ |
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
// +build none
|
||||||
|
|
||||||
|
// This program generates contract/code.go, which contains the chequebook code
|
||||||
|
// after deployment.
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io/ioutil" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind" |
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" |
||||||
|
"github.com/ethereum/go-ethereum/contracts/chequebook/contract" |
||||||
|
"github.com/ethereum/go-ethereum/core" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") |
||||||
|
testAccount = core.GenesisAccount{ |
||||||
|
Address: crypto.PubkeyToAddress(testKey.PublicKey), |
||||||
|
Balance: big.NewInt(500000000000), |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
backend := backends.NewSimulatedBackend(testAccount) |
||||||
|
auth := bind.NewKeyedTransactor(testKey) |
||||||
|
|
||||||
|
// Deploy the contract, get the code.
|
||||||
|
addr, _, _, err := contract.DeployChequebook(auth, backend) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
backend.Commit() |
||||||
|
code, err := backend.CodeAt(nil, addr, nil) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
if len(code) == 0 { |
||||||
|
panic("empty code") |
||||||
|
} |
||||||
|
|
||||||
|
// Write the output file.
|
||||||
|
content := fmt.Sprintf(`// TODO: insert license header
|
||||||
|
|
||||||
|
package contract |
||||||
|
|
||||||
|
// ContractDeployedCode is used to detect suicides. This constant needs to be
|
||||||
|
// updated when the contract code is changed.
|
||||||
|
const ContractDeployedCode = "%#x" |
||||||
|
`, code) |
||||||
|
if err := ioutil.WriteFile("contract/code.go", []byte(content), 0644); err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue