@@ -21,6 +21,7 @@ import (
2121
2222 "github.com/ethereum/go-ethereum/accounts/abi/bind"
2323 "github.com/ethereum/go-ethereum/common"
24+ "github.com/ethereum/go-ethereum/core/types"
2425 "github.com/ethereum/go-ethereum/metrics"
2526 contract "github.com/ethersphere/swarm/contracts/swap"
2627 "github.com/ethersphere/swarm/swap/chain"
@@ -30,10 +31,9 @@ import (
3031// CashChequeBeneficiaryTransactionCost is the expected gas cost of a CashChequeBeneficiary transaction
3132const CashChequeBeneficiaryTransactionCost = 50000
3233
33- // CashoutProcessor holds all relevant fields needed for processing cashouts
34- type CashoutProcessor struct {
35- backend chain.Backend // ethereum backend to use
36- privateKey * ecdsa.PrivateKey // private key to use
34+ var CashoutRequestTypeID = chain.TxRequestTypeID {
35+ Handler : "cashout" ,
36+ RequestType : "CashoutRequest" ,
3737}
3838
3939// CashoutRequest represents a request for a cashout operation
@@ -42,42 +42,94 @@ type CashoutRequest struct {
4242 Destination common.Address // destination for the payout
4343}
4444
45- // ActiveCashout stores the necessary information for a cashout in progess
46- type ActiveCashout struct {
47- Request CashoutRequest // the request that caused this cashout
48- TransactionHash common.Hash // the hash of the current transaction for this request
45+ // CashoutProcessor holds all relevant fields needed for processing cashouts
46+ type CashoutProcessor struct {
47+ backend chain.Backend // ethereum backend to use
48+ txScheduler chain.TxScheduler // transaction queue to use
49+ cashoutResultHandler CashoutResultHandler
50+ cashoutDone chan * CashoutRequest
51+ }
52+
53+ type CashoutResultHandler interface {
54+ HandleCashoutResult (request * CashoutRequest , result * contract.CashChequeResult , receipt * types.Receipt ) error
4955}
5056
5157// newCashoutProcessor creates a new instance of CashoutProcessor
52- func newCashoutProcessor (backend chain.Backend , privateKey * ecdsa.PrivateKey ) * CashoutProcessor {
53- return & CashoutProcessor {
54- backend : backend ,
55- privateKey : privateKey ,
58+ func newCashoutProcessor (txScheduler chain.TxScheduler , backend chain.Backend , privateKey * ecdsa.PrivateKey , cashoutResultHandler CashoutResultHandler ) * CashoutProcessor {
59+ c := & CashoutProcessor {
60+ backend : backend ,
61+ txScheduler : txScheduler ,
62+ cashoutResultHandler : cashoutResultHandler ,
5663 }
57- }
5864
59- // cashCheque tries to cash the cheque specified in the request
60- // after the transaction is sent it waits on its success
61- func (c * CashoutProcessor ) cashCheque (ctx context.Context , request * CashoutRequest ) error {
62- cheque := request .Cheque
63- opts := bind .NewKeyedTransactor (c .privateKey )
64- opts .Context = ctx
65+ txScheduler .SetHandlers (CashoutRequestTypeID , & chain.TxRequestHandlers {
66+ Send : func (id uint64 , backend chain.Backend , opts * bind.TransactOpts ) (common.Hash , error ) {
67+ var request CashoutRequest
68+ if err := c .txScheduler .GetRequest (id , & request ); err != nil {
69+ return common.Hash {}, err
70+ }
71+
72+ cheque := request .Cheque
73+
74+ otherSwap , err := contract .InstanceAt (cheque .Contract , backend )
75+ if err != nil {
76+ return common.Hash {}, err
77+ }
78+
79+ tx , err := otherSwap .CashChequeBeneficiaryStart (opts , request .Destination , cheque .CumulativePayout , cheque .Signature )
80+ if err != nil {
81+ return common.Hash {}, err
82+ }
83+ return tx .Hash (), nil
84+ },
85+ NotifyReceipt : func (ctx context.Context , id uint64 , notification * chain.TxReceiptNotification ) error {
86+ var request * CashoutRequest
87+ err := c .txScheduler .GetRequest (id , & request )
88+ if err != nil {
89+ return err
90+ }
91+
92+ otherSwap , err := contract .InstanceAt (request .Cheque .Contract , c .backend )
93+ if err != nil {
94+ return err
95+ }
96+
97+ receipt := & notification .Receipt
98+ if receipt .Status == 0 {
99+ swapLog .Error ("cheque cashing transaction reverted" , "tx" , receipt .TxHash )
100+ return nil
101+ }
102+
103+ result := otherSwap .CashChequeBeneficiaryResult (receipt )
104+ return c .cashoutResultHandler .HandleCashoutResult (request , result , receipt )
105+ },
106+ })
107+ return c
108+ }
65109
66- otherSwap , err := contract .InstanceAt (cheque .Contract , c .backend )
110+ func (c * CashoutProcessor ) submitCheque (ctx context.Context , request * CashoutRequest ) {
111+ expectedPayout , transactionCosts , err := c .estimatePayout (ctx , & request .Cheque )
67112 if err != nil {
68- return err
113+ swapLog .Error ("could not estimate payout" , "error" , err )
114+ return
69115 }
70116
71- tx , err := otherSwap .CashChequeBeneficiaryStart (opts , request .Destination , cheque .CumulativePayout , cheque .Signature )
117+ costsMultiplier := uint256 .FromUint64 (2 )
118+ costThreshold , err := uint256 .New ().Mul (transactionCosts , costsMultiplier )
72119 if err != nil {
73- return err
120+ swapLog .Error ("overflow in transaction fee" , "error" , err )
121+ return
74122 }
75123
76- // this blocks until the cashout has been successfully processed
77- return c .waitForAndProcessActiveCashout (& ActiveCashout {
78- Request : * request ,
79- TransactionHash : tx .Hash (),
80- })
124+ // do a payout transaction if we get 2 times the gas costs
125+ if expectedPayout .Cmp (costThreshold ) == 1 {
126+ swapLog .Info ("queueing cashout" , "cheque" , & request .Cheque )
127+ _ , err := c .txScheduler .ScheduleRequest (CashoutRequestTypeID , request )
128+ if err != nil {
129+ metrics .GetOrRegisterCounter ("swap.cheques.cashed.errors" , nil ).Inc (1 )
130+ swapLog .Error ("cashing cheque:" , "error" , err )
131+ }
132+ }
81133}
82134
83135// estimatePayout estimates the payout for a given cheque as well as the transaction cost
@@ -123,31 +175,3 @@ func (c *CashoutProcessor) estimatePayout(ctx context.Context, cheque *Cheque) (
123175
124176 return expectedPayout , transactionCosts , nil
125177}
126-
127- // waitForAndProcessActiveCashout waits for activeCashout to complete
128- func (c * CashoutProcessor ) waitForAndProcessActiveCashout (activeCashout * ActiveCashout ) error {
129- ctx , cancel := context .WithTimeout (context .Background (), DefaultTransactionTimeout )
130- defer cancel ()
131-
132- receipt , err := chain .WaitMined (ctx , c .backend , activeCashout .TransactionHash )
133- if err != nil {
134- return err
135- }
136-
137- otherSwap , err := contract .InstanceAt (activeCashout .Request .Cheque .Contract , c .backend )
138- if err != nil {
139- return err
140- }
141-
142- result := otherSwap .CashChequeBeneficiaryResult (receipt )
143-
144- metrics .GetOrRegisterCounter ("swap.cheques.cashed.honey" , nil ).Inc (result .TotalPayout .Int64 ())
145-
146- if result .Bounced {
147- metrics .GetOrRegisterCounter ("swap.cheques.cashed.bounced" , nil ).Inc (1 )
148- swapLog .Warn ("cheque bounced" , "tx" , receipt .TxHash )
149- }
150-
151- swapLog .Info ("cheque cashed" , "honey" , activeCashout .Request .Cheque .Honey )
152- return nil
153- }
0 commit comments