Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

Commit

Permalink
contract, swap, swap/txqueue: move backend into txqueue package and p…
Browse files Browse the repository at this point in the history
…ass hash to WaitMined directly
  • Loading branch information
ralph-pichler committed Feb 6, 2020
1 parent a638f1b commit 4dd76ef
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 79 deletions.
7 changes: 4 additions & 3 deletions contracts/swap/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
chequebookFactory "github.com/ethersphere/go-sw3/contracts-v0-2-0/simpleswapfactory"
"github.com/ethersphere/swarm/swap/txqueue"
)

var (
Expand All @@ -26,7 +27,7 @@ var (
type simpleSwapFactory struct {
instance *chequebookFactory.SimpleSwapFactory
address common.Address
backend Backend
backend txqueue.Backend
}

// SimpleSwapFactory interface defines the methods available for a factory contract for SimpleSwap
Expand All @@ -40,7 +41,7 @@ type SimpleSwapFactory interface {
}

// FactoryAt creates a SimpleSwapFactory instance for the given address and backend
func FactoryAt(address common.Address, backend Backend) (SimpleSwapFactory, error) {
func FactoryAt(address common.Address, backend txqueue.Backend) (SimpleSwapFactory, error) {
simple, err := chequebookFactory.NewSimpleSwapFactory(address, backend)
if err != nil {
return nil, err
Expand Down Expand Up @@ -83,7 +84,7 @@ func (sf simpleSwapFactory) DeploySimpleSwap(auth *bind.TransactOpts, issuer com
return nil, err
}

receipt, err := WaitFunc(auth.Context, sf.backend, tx)
receipt, err := txqueue.WaitFunc(auth.Context, sf.backend, tx.Hash())
if err != nil {
return nil, err
}
Expand Down
64 changes: 5 additions & 59 deletions contracts/swap/swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,17 @@
package swap

import (
"context"
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
contract "github.com/ethersphere/go-sw3/contracts-v0-2-0/erc20simpleswap"
"github.com/ethersphere/swarm/swap/txqueue"
"github.com/ethersphere/swarm/uint256"
)

var (
// ErrTransactionReverted is given when the transaction that cashes a cheque is reverted
ErrTransactionReverted = errors.New("Transaction reverted")
)

// Backend wraps all methods required for contract deployment.
type Backend interface {
bind.ContractBackend
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error)
}

// Contract interface defines the methods exported from the underlying go-bindings for the smart contract
type Contract interface {
// Withdraw attempts to withdraw ERC20-token from the chequebook
Expand Down Expand Up @@ -89,13 +76,13 @@ type Params struct {
type simpleContract struct {
instance *contract.ERC20SimpleSwap
address common.Address
backend Backend
backend txqueue.Backend
}

// InstanceAt creates a new instance of a contract at a specific address.
// It assumes that there is an existing contract instance at the given address, or an error is returned
// This function is needed to communicate with remote Swap contracts (e.g. sending a cheque)
func InstanceAt(address common.Address, backend Backend) (Contract, error) {
func InstanceAt(address common.Address, backend txqueue.Backend) (Contract, error) {
instance, err := contract.NewERC20SimpleSwap(address, backend)
if err != nil {
return nil, err
Expand All @@ -110,7 +97,7 @@ func (s simpleContract) Withdraw(auth *bind.TransactOpts, amount *big.Int) (*typ
if err != nil {
return nil, err
}
return WaitFunc(auth.Context, s.backend, tx)
return txqueue.WaitFunc(auth.Context, s.backend, tx.Hash())
}

// Deposit sends an amount in ERC20 token to the chequebook and blocks until the transaction is mined
Expand Down Expand Up @@ -140,7 +127,7 @@ func (s simpleContract) Deposit(auth *bind.TransactOpts, amount *big.Int) (*type
if err != nil {
return nil, err
}
return WaitFunc(auth.Context, s.backend, tx)
return txqueue.WaitFunc(auth.Context, s.backend, tx.Hash())
}

// CashChequeBeneficiaryStart sends the transaction to cash a cheque as the beneficiary
Expand Down Expand Up @@ -224,44 +211,3 @@ func (s simpleContract) Issuer(opts *bind.CallOpts) (common.Address, error) {
func (s simpleContract) PaidOut(opts *bind.CallOpts, addr common.Address) (*big.Int, error) {
return s.instance.PaidOut(opts, addr)
}

// WaitFunc is the default function to wait for transactions
// We can overwrite this in tests so that we don't need to wait for mining
var WaitFunc = waitForTx

// waitForTx waits for transaction to be mined and returns the receipt
func waitForTx(ctx context.Context, backend Backend, tx *types.Transaction) (*types.Receipt, error) {
// it blocks here until tx is mined
receipt, err := bind.WaitMined(ctx, backend, tx)
if err != nil {
return nil, err
}
// indicate whether the transaction did not revert
if receipt.Status != types.ReceiptStatusSuccessful {
return nil, ErrTransactionReverted
}
return receipt, nil
}

// WaitForTransactionByHash waits for a transaction to by mined by hash
func WaitForTransactionByHash(ctx context.Context, backend Backend, txHash common.Hash) (*types.Receipt, error) {
tx, pending, err := backend.TransactionByHash(ctx, txHash)
if err != nil {
return nil, err
}

var receipt *types.Receipt
if pending {
receipt, err = WaitFunc(ctx, backend, tx)
if err != nil {
return nil, err
}
} else {
receipt, err = backend.TransactionReceipt(ctx, txHash)
if err != nil {
return nil, err
}
}

return receipt, nil
}
7 changes: 4 additions & 3 deletions swap/cashout.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/metrics"
contract "github.com/ethersphere/swarm/contracts/swap"
"github.com/ethersphere/swarm/swap/txqueue"
"github.com/ethersphere/swarm/uint256"
)

Expand All @@ -31,7 +32,7 @@ const CashChequeBeneficiaryTransactionCost = 50000

// CashoutProcessor holds all relevant fields needed for processing cashouts
type CashoutProcessor struct {
backend contract.Backend // ethereum backend to use
backend txqueue.Backend // ethereum backend to use
privateKey *ecdsa.PrivateKey // private key to use
}

Expand All @@ -48,7 +49,7 @@ type ActiveCashout struct {
}

// newCashoutProcessor creates a new instance of CashoutProcessor
func newCashoutProcessor(backend contract.Backend, privateKey *ecdsa.PrivateKey) *CashoutProcessor {
func newCashoutProcessor(backend txqueue.Backend, privateKey *ecdsa.PrivateKey) *CashoutProcessor {
return &CashoutProcessor{
backend: backend,
privateKey: privateKey,
Expand Down Expand Up @@ -128,7 +129,7 @@ func (c *CashoutProcessor) waitForAndProcessActiveCashout(activeCashout *ActiveC
ctx, cancel := context.WithTimeout(context.Background(), DefaultTransactionTimeout)
defer cancel()

receipt, err := contract.WaitForTransactionByHash(ctx, c.backend, activeCashout.TransactionHash)
receipt, err := txqueue.WaitFunc(ctx, c.backend, activeCashout.TransactionHash)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions swap/cashout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/log"
contract "github.com/ethersphere/swarm/contracts/swap"
"github.com/ethersphere/swarm/swap/txqueue"
"github.com/ethersphere/swarm/uint256"
)

Expand Down Expand Up @@ -55,7 +55,7 @@ func TestContractIntegration(t *testing.T) {
t.Fatal(err)
}

receipt, err := contract.WaitForTransactionByHash(context.Background(), backend, tx.Hash())
receipt, err := txqueue.WaitFunc(context.Background(), backend, tx.Hash())
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestContractIntegration(t *testing.T) {
t.Fatal(err)
}

receipt, err = contract.WaitForTransactionByHash(context.Background(), backend, tx.Hash())
receipt, err = txqueue.WaitFunc(context.Background(), backend, tx.Hash())
if err != nil {
t.Fatal(err)
}
Expand Down
15 changes: 8 additions & 7 deletions swap/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/ethersphere/swarm/network"
"github.com/ethersphere/swarm/p2p/protocols"
"github.com/ethersphere/swarm/state"
"github.com/ethersphere/swarm/swap/txqueue"
"github.com/ethersphere/swarm/uint256"
)

Expand Down Expand Up @@ -226,7 +227,7 @@ func testCashCheque(s *Swap, cheque *Cheque) {
}

// when testing, we don't need to wait for a transaction to be mined
func testWaitForTx(ctx context.Context, backend cswap.Backend, tx *types.Transaction) (*types.Receipt, error) {
func testWaitForTx(ctx context.Context, backend txqueue.Backend, txHash common.Hash) (*types.Receipt, error) {

var stb *swapTestBackend
var ok bool
Expand All @@ -235,12 +236,12 @@ func testWaitForTx(ctx context.Context, backend cswap.Backend, tx *types.Transac
}
stb.Commit()

receipt, err := backend.TransactionReceipt(ctx, tx.Hash())
receipt, err := backend.TransactionReceipt(ctx, txHash)
if err != nil {
return nil, err
}
if receipt.Status != types.ReceiptStatusSuccessful {
return nil, cswap.ErrTransactionReverted
return nil, txqueue.ErrTransactionReverted
}
return receipt, nil
}
Expand All @@ -250,21 +251,21 @@ func testWaitForTx(ctx context.Context, backend cswap.Backend, tx *types.Transac
func setupContractTest() func() {
// we overwrite the waitForTx function with one which the simulated backend
// immediately commits
currentWaitFunc := cswap.WaitFunc
currentWaitFunc := txqueue.WaitFunc
// we also need to store the previous cashCheque function in case this is called multiple times
currentCashCheque := defaultCashCheque
defaultCashCheque = testCashCheque
// overwrite only for the duration of the test, so...
cswap.WaitFunc = testWaitForTx
txqueue.WaitFunc = testWaitForTx
return func() {
// ...we need to set it back to original when done
cswap.WaitFunc = currentWaitFunc
txqueue.WaitFunc = currentWaitFunc
defaultCashCheque = currentCashCheque
}
}

// deploy for testing (needs simulated backend commit)
func testDeployWithPrivateKey(ctx context.Context, backend cswap.Backend, privateKey *ecdsa.PrivateKey, ownerAddress common.Address, depositAmount *uint256.Uint256) (cswap.Contract, error) {
func testDeployWithPrivateKey(ctx context.Context, backend txqueue.Backend, privateKey *ecdsa.PrivateKey, ownerAddress common.Address, depositAmount *uint256.Uint256) (cswap.Contract, error) {
opts := bind.NewKeyedTransactor(privateKey)
opts.Context = ctx

Expand Down
9 changes: 5 additions & 4 deletions swap/swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/ethersphere/swarm/network"
"github.com/ethersphere/swarm/p2p/protocols"
"github.com/ethersphere/swarm/state"
"github.com/ethersphere/swarm/swap/txqueue"
"github.com/ethersphere/swarm/uint256"
)

Expand All @@ -60,7 +61,7 @@ type Swap struct {
peers map[enode.ID]*Peer // map of all swap Peers
peersLock sync.RWMutex // lock for peers map
owner *Owner // contract access
backend contract.Backend // the backend (blockchain) used
backend txqueue.Backend // the backend (blockchain) used
chainID uint64 // id of the chain the backend is connected to
params *Params // economic and operational parameters
contract contract.Contract // reference to the smart contract
Expand Down Expand Up @@ -135,7 +136,7 @@ func swapRotatingFileHandler(logdir string) (log.Handler, error) {
}

// newSwapInstance is a swap constructor function without integrity checks
func newSwapInstance(stateStore state.Store, owner *Owner, backend contract.Backend, chainID uint64, params *Params, chequebookFactory contract.SimpleSwapFactory) *Swap {
func newSwapInstance(stateStore state.Store, owner *Owner, backend txqueue.Backend, chainID uint64, params *Params, chequebookFactory contract.SimpleSwapFactory) *Swap {
return &Swap{
store: stateStore,
peers: make(map[enode.ID]*Peer),
Expand Down Expand Up @@ -246,7 +247,7 @@ const (
)

// createFactory determines the factory address and returns and error if no factory address has been specified or is unknown for the network
func createFactory(factoryAddress common.Address, chainID *big.Int, backend contract.Backend) (factory swap.SimpleSwapFactory, err error) {
func createFactory(factoryAddress common.Address, chainID *big.Int, backend txqueue.Backend) (factory swap.SimpleSwapFactory, err error) {
if (factoryAddress == common.Address{}) {
if factoryAddress, err = contract.FactoryAddressForNetwork(chainID.Uint64()); err != nil {
return nil, err
Expand Down Expand Up @@ -497,7 +498,7 @@ func cashCheque(s *Swap, cheque *Cheque) {

if err != nil {
metrics.GetOrRegisterCounter("swap.cheques.cashed.errors", nil).Inc(1)
swapLog.Error("cashing cheque:", err)
swapLog.Error("cashing cheque:", "error", err)
}
}

Expand Down
54 changes: 54 additions & 0 deletions swap/txqueue/txqueue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package txqueue

import (
"context"
"errors"
"time"

"github.com/ethereum/go-ethereum/log"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

var (
// ErrTransactionReverted is given when the transaction that cashes a cheque is reverted
ErrTransactionReverted = errors.New("Transaction reverted")
)

// Backend wraps all methods required for contract deployment.
type Backend interface {
bind.ContractBackend
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error)
}

// WaitFunc is the default function used for waiting for a receipt of a transaction
// this is overridden during in tests to automatically create a block on wait
var WaitFunc = func(ctx context.Context, b Backend, hash common.Hash) (*types.Receipt, error) {
queryTicker := time.NewTicker(time.Second)
defer queryTicker.Stop()

for {
receipt, err := b.TransactionReceipt(ctx, hash)
if receipt != nil {
// indicate whether the transaction did not revert
if receipt.Status != types.ReceiptStatusSuccessful {
return nil, ErrTransactionReverted
}
return receipt, nil
}
if err != nil {
log.Trace("Receipt retrieval failed", "err", err)
} else {
log.Trace("Transaction not yet mined")
}
// Wait for the next round.
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-queryTicker.C:
}
}
}

0 comments on commit 4dd76ef

Please sign in to comment.