Skip to content

Commit 08cd528

Browse files
swap, contracts, vendor: move to waiver-free simplestswap (ethersphere#1683)
* swap: use newer simple swap contract * swap: check if cheque bounced * swap: increase balance by honey on sendCheque
1 parent 403e54e commit 08cd528

File tree

13 files changed

+460
-714
lines changed

13 files changed

+460
-714
lines changed

contracts/swap/contract/code.go

Lines changed: 0 additions & 21 deletions
This file was deleted.

contracts/swap/swap.go

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ import (
2929
"github.com/ethereum/go-ethereum/accounts/abi/bind"
3030
"github.com/ethereum/go-ethereum/common"
3131
"github.com/ethereum/go-ethereum/core/types"
32-
"github.com/ethersphere/swarm/contracts/swap/contract"
32+
contract "github.com/ethersphere/go-sw3/contracts-v0-1-0/simpleswap"
3333
)
3434

3535
var (
3636
// ErrNotASwapContract is given when an address is verified not to have a SWAP contract based on its bytecode
3737
ErrNotASwapContract = errors.New("not a swap contract")
38-
// ErrTransactionReverted is given when the transaction that submits or cashes a cheque is reverted
38+
// ErrTransactionReverted is given when the transaction that cashes a cheque is reverted
3939
ErrTransactionReverted = errors.New("Transaction reverted")
4040
)
4141

@@ -48,7 +48,7 @@ type Backend interface {
4848
// Deploy deploys an instance of the underlying contract and returns its `Contract` abstraction
4949
func Deploy(auth *bind.TransactOpts, backend bind.ContractBackend, owner common.Address, harddepositTimeout time.Duration) (common.Address, Contract, *types.Transaction, error) {
5050
addr, tx, s, err := contract.DeploySimpleSwap(auth, backend, owner, big.NewInt(int64(harddepositTimeout)))
51-
c := simpleContract{instance: s}
51+
c := simpleContract{instance: s, address: addr}
5252
return addr, c, tx, err
5353
}
5454

@@ -57,30 +57,31 @@ func Deploy(auth *bind.TransactOpts, backend bind.ContractBackend, owner common.
5757
// This function is needed to communicate with remote Swap contracts (e.g. sending a cheque)
5858
func InstanceAt(address common.Address, backend bind.ContractBackend) (Contract, error) {
5959
simple, err := contract.NewSimpleSwap(address, backend)
60-
c := simpleContract{instance: simple}
60+
c := simpleContract{instance: simple, address: address}
6161
return c, err
6262
}
6363

6464
// Contract interface defines the methods exported from the underlying go-bindings for the smart contract
6565
type Contract interface {
66-
// SubmitChequeBeneficiary submits a cheque to a given beneficiary
67-
SubmitChequeBeneficiary(opts *bind.TransactOpts, backend Backend, serial *big.Int, amount *big.Int, timeout *big.Int, ownerSig []byte) (*types.Receipt, error)
6866
// CashChequeBeneficiary cashes the cheque by the beneficiary
69-
CashChequeBeneficiary(auth *bind.TransactOpts, backend Backend, beneficiary common.Address, requestPayout *big.Int) (*types.Receipt, error)
67+
CashChequeBeneficiary(auth *bind.TransactOpts, backend Backend, beneficiary common.Address, cumulativePayout *big.Int, ownerSig []byte) (*CashChequeResult, *types.Receipt, error)
7068
// ContractParams returns contract info (e.g. deployed address)
7169
ContractParams() *Params
7270
// Issuer returns the contract owner from the blockchain
7371
Issuer(opts *bind.CallOpts) (common.Address, error)
74-
// Cheques returns the last cheque for the given address
75-
Cheques(opts *bind.CallOpts, addr common.Address) (*ChequeResult, error)
72+
// PaidOut returns the total paid out amount for the given address
73+
PaidOut(opts *bind.CallOpts, addr common.Address) (*big.Int, error)
7674
}
7775

78-
// ChequeResult is needed because the underlying `Cheques` method returns an untyped struct
79-
type ChequeResult struct {
80-
Serial *big.Int
81-
Amount *big.Int
82-
PaidOut *big.Int
83-
CashTimeout *big.Int
76+
// CashChequeResult summarizes the result of a CashCheque or CashChequeBeneficiary call
77+
type CashChequeResult struct {
78+
Beneficiary common.Address // beneficiary of the cheque
79+
Recipient common.Address // address which received the funds
80+
Caller common.Address // caller of cashCheque
81+
TotalPayout *big.Int // total amount that was paid out in this call
82+
CumulativePayout *big.Int // cumulative payout of the cheque that was cashed
83+
CallerPayout *big.Int // payout for the caller of cashCheque
84+
Bounced bool // indicates wether parts of the cheque bounced
8485
}
8586

8687
// Params encapsulates some contract parameters (currently mostly informational)
@@ -95,7 +96,7 @@ func ValidateCode(ctx context.Context, b bind.ContractBackend, address common.Ad
9596
if err != nil {
9697
return err
9798
}
98-
referenceCode := common.FromHex(contract.ContractDeployedCode)
99+
referenceCode := common.FromHex(contract.SimpleSwapDeployedCode)
99100
if !bytes.Equal(codeReadFromAddress, referenceCode) {
100101
return ErrNotASwapContract
101102
}
@@ -122,6 +123,7 @@ func waitForTx(auth *bind.TransactOpts, backend Backend, tx *types.Transaction)
122123

123124
type simpleContract struct {
124125
instance *contract.SimpleSwap
126+
address common.Address
125127
}
126128

127129
// ContractParams returns contract information
@@ -132,40 +134,46 @@ func (s simpleContract) ContractParams() *Params {
132134
}
133135
}
134136

135-
// Cheques returns the last cheque from the smart contract
136-
func (s simpleContract) Cheques(opts *bind.CallOpts, addr common.Address) (*ChequeResult, error) {
137-
r, err := s.instance.Cheques(opts, addr)
138-
if err != nil {
139-
return nil, err
140-
}
141-
result := &ChequeResult{
142-
Serial: r.Serial,
143-
Amount: r.Amount,
144-
PaidOut: r.PaidOut,
145-
CashTimeout: r.CashTimeout,
146-
}
147-
return result, nil
137+
// PaidOut returns the total paid out amount for the given address
138+
func (s simpleContract) PaidOut(opts *bind.CallOpts, addr common.Address) (*big.Int, error) {
139+
return s.instance.PaidOut(opts, addr)
148140
}
149141

150142
// Issuer returns the contract owner from the blockchain
151143
func (s simpleContract) Issuer(opts *bind.CallOpts) (common.Address, error) {
152144
return s.instance.Issuer(opts)
153145
}
154146

155-
// SubmitChequeBeneficiary prepares to send a call to submitChequeBeneficiary and blocks until the transaction is mined.
156-
func (s simpleContract) SubmitChequeBeneficiary(auth *bind.TransactOpts, backend Backend, serial *big.Int, amount *big.Int, timeout *big.Int, ownerSig []byte) (*types.Receipt, error) {
157-
tx, err := s.instance.SubmitChequeBeneficiary(auth, serial, amount, timeout, ownerSig)
147+
// CashChequeBeneficiary cashes the cheque on the blockchain and blocks until the transaction is mined.
148+
func (s simpleContract) CashChequeBeneficiary(auth *bind.TransactOpts, backend Backend, beneficiary common.Address, cumulativePayout *big.Int, ownerSig []byte) (*CashChequeResult, *types.Receipt, error) {
149+
tx, err := s.instance.CashChequeBeneficiary(auth, beneficiary, cumulativePayout, ownerSig)
158150
if err != nil {
159-
return nil, err
151+
return nil, nil, err
160152
}
161-
return WaitFunc(auth, backend, tx)
162-
}
163-
164-
// CashChequeBeneficiary cashes the cheque.
165-
func (s simpleContract) CashChequeBeneficiary(auth *bind.TransactOpts, backend Backend, beneficiary common.Address, requestPayout *big.Int) (*types.Receipt, error) {
166-
tx, err := s.instance.CashChequeBeneficiary(auth, beneficiary, requestPayout)
153+
receipt, err := WaitFunc(auth, backend, tx)
167154
if err != nil {
168-
return nil, err
155+
return nil, nil, err
156+
}
157+
158+
result := &CashChequeResult{
159+
Bounced: false,
169160
}
170-
return WaitFunc(auth, backend, tx)
161+
162+
for _, log := range receipt.Logs {
163+
if log.Address != s.address {
164+
continue
165+
}
166+
if event, err := s.instance.ParseChequeCashed(*log); err == nil {
167+
result.Beneficiary = event.Beneficiary
168+
result.Caller = event.Caller
169+
result.CallerPayout = event.CallerPayout
170+
result.TotalPayout = event.TotalPayout
171+
result.CumulativePayout = event.CumulativePayout
172+
result.Recipient = event.Recipient
173+
} else if _, err := s.instance.ParseChequeBounced(*log); err == nil {
174+
result.Bounced = true
175+
}
176+
}
177+
178+
return result, receipt, nil
171179
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ require (
2626
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c // indirect
2727
github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa // indirect
2828
github.com/ethereum/go-ethereum v1.9.2
29+
github.com/ethersphere/go-sw3 v0.1.0
2930
github.com/fatih/color v1.7.0 // indirect
3031
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc
3132
github.com/gballet/go-libpcsclite v0.0.0-20190528105824-2fd9b619dd3c // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy
6262
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
6363
github.com/ethereum/go-ethereum v1.9.2 h1:RMIHDO/diqXEgORSVzYx8xW9x2+S32PoAX5lQwya0Lw=
6464
github.com/ethereum/go-ethereum v1.9.2/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY=
65+
github.com/ethersphere/go-sw3 v0.1.0 h1:XMaWxiBhFtrxfOel2tXmxuan3eCfFw/NcftV6sUVtIc=
66+
github.com/ethersphere/go-sw3 v0.1.0/go.mod h1:HukT0aZ6QdW/d7zuD/0g5xlw6ewu9QeqHojxLDsaERQ=
6567
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
6668
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
6769
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=

swap/cheque.go

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,21 @@ import (
2626
"github.com/ethereum/go-ethereum/crypto"
2727
)
2828

29-
// encodeForSignature encodes the cheque in the format used in the signing procedure
30-
func (cheque *Cheque) encodeForSignature() []byte {
31-
serialBytes := make([]byte, 32)
32-
amountBytes := make([]byte, 32)
33-
timeoutBytes := make([]byte, 32)
29+
// encodeForSignature encodes the cheque params in the format used in the signing procedure
30+
func (cheque *ChequeParams) encodeForSignature() []byte {
31+
cumulativePayoutBytes := make([]byte, 32)
3432
// we need to write the last 8 bytes as we write a uint64 into a 32-byte array
3533
// encoded in BigEndian because EVM uses BigEndian encoding
36-
binary.BigEndian.PutUint64(serialBytes[24:], cheque.Serial)
37-
binary.BigEndian.PutUint64(amountBytes[24:], cheque.Amount)
38-
binary.BigEndian.PutUint64(timeoutBytes[24:], cheque.Timeout)
34+
binary.BigEndian.PutUint64(cumulativePayoutBytes[24:], cheque.CumulativePayout)
3935
// construct the actual cheque
4036
input := cheque.Contract.Bytes()
4137
input = append(input, cheque.Beneficiary.Bytes()...)
42-
input = append(input, serialBytes[:]...)
43-
input = append(input, amountBytes[:]...)
44-
input = append(input, timeoutBytes[:]...)
45-
38+
input = append(input, cumulativePayoutBytes[:]...)
4639
return input
4740
}
4841

49-
// sigHash hashes the cheque using the prefix that would be added by eth_Sign
50-
func (cheque *Cheque) sigHash() []byte {
42+
// sigHash hashes the cheque params using the prefix that would be added by eth_Sign
43+
func (cheque *ChequeParams) sigHash() []byte {
5144
// we can ignore the error because it is always nil
5245
encoded := cheque.encodeForSignature()
5346
input := crypto.Keccak256(encoded)
@@ -84,7 +77,7 @@ func (cheque *Cheque) VerifySig(expectedSigner common.Address) error {
8477
}
8578

8679
// Sign returns the cheque's signature with supplied private key
87-
func (cheque *Cheque) Sign(prv *ecdsa.PrivateKey) ([]byte, error) {
80+
func (cheque *ChequeParams) Sign(prv *ecdsa.PrivateKey) ([]byte, error) {
8881
sig, err := crypto.Sign(cheque.sigHash(), prv)
8982
if err != nil {
9083
return nil, err
@@ -97,19 +90,11 @@ func (cheque *Cheque) Sign(prv *ecdsa.PrivateKey) ([]byte, error) {
9790

9891
// Equal checks if other has the same fields
9992
func (cheque *Cheque) Equal(other *Cheque) bool {
100-
if cheque.Serial != other.Serial {
101-
return false
102-
}
103-
10493
if cheque.Beneficiary != other.Beneficiary {
10594
return false
10695
}
10796

108-
if cheque.Amount != other.Amount {
109-
return false
110-
}
111-
112-
if cheque.Timeout != other.Timeout {
97+
if cheque.CumulativePayout != other.CumulativePayout {
11398
return false
11499
}
115100

@@ -140,29 +125,20 @@ func (cheque *Cheque) verifyChequeProperties(p *Peer, expectedBeneficiary common
140125
return fmt.Errorf("wrong cheque parameters: expected beneficiary: %x, was: %x", expectedBeneficiary, cheque.Beneficiary)
141126
}
142127

143-
if cheque.Timeout != 0 {
144-
return fmt.Errorf("wrong cheque parameters: expected timeout to be 0, was: %d", cheque.Timeout)
145-
}
146-
147128
return nil
148129
}
149130

150-
// verifyChequeAgainstLast verifies that serial and amount are higher than in the previous cheque
151-
// furthermore it cheques that the increase in amount is as expected
131+
// verifyChequeAgainstLast verifies that the amount is higher than in the previous cheque and the increase is as expected
152132
// returns the actual amount received in this cheque
153133
func (cheque *Cheque) verifyChequeAgainstLast(lastCheque *Cheque, expectedAmount uint64) (uint64, error) {
154-
actualAmount := cheque.Amount
134+
actualAmount := cheque.CumulativePayout
155135

156136
if lastCheque != nil {
157-
if cheque.Serial <= lastCheque.Serial {
158-
return 0, fmt.Errorf("wrong cheque parameters: expected serial larger than %d, was: %d", lastCheque.Serial, cheque.Serial)
159-
}
160-
161-
if cheque.Amount <= lastCheque.Amount {
162-
return 0, fmt.Errorf("wrong cheque parameters: expected amount larger than %d, was: %d", lastCheque.Amount, cheque.Amount)
137+
if cheque.CumulativePayout <= lastCheque.CumulativePayout {
138+
return 0, fmt.Errorf("wrong cheque parameters: expected cumulative payout larger than %d, was: %d", lastCheque.CumulativePayout, cheque.CumulativePayout)
163139
}
164140

165-
actualAmount -= lastCheque.Amount
141+
actualAmount -= lastCheque.CumulativePayout
166142
}
167143

168144
if expectedAmount != actualAmount {

swap/config.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ const (
3030
deployRetries = 5
3131
// delay between retries
3232
deployDelay = 1 * time.Second
33-
// Should be non-zero once we implement waivers
34-
defaultCashInDelay = uint64(0)
3533
// This is the amount of time in seconds which an issuer has to wait to decrease the harddeposit of a beneficiary.
3634
// The smart-contract allows for setting this variable differently per beneficiary
3735
defaultHarddepositTimeoutDuration = 24 * time.Hour

swap/protocol_test.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,11 @@ func TestEmitCheque(t *testing.T) {
146146
log.Debug("create a cheque")
147147
cheque := &Cheque{
148148
ChequeParams: ChequeParams{
149-
Contract: debitorSwap.owner.Contract,
150-
Beneficiary: creditorSwap.owner.address,
151-
Amount: 42,
152-
Honey: 42,
153-
Timeout: 0,
149+
Contract: debitorSwap.owner.Contract,
150+
Beneficiary: creditorSwap.owner.address,
151+
CumulativePayout: 42,
154152
},
153+
Honey: 42,
155154
}
156155
cheque.Signature, err = cheque.Sign(debitorSwap.owner.privateKey)
157156
if err != nil {
@@ -166,17 +165,17 @@ func TestEmitCheque(t *testing.T) {
166165
defer cleanup()
167166

168167
// now we need to create the channel...
169-
testBackend.submitDone = make(chan struct{})
168+
testBackend.cashDone = make(chan struct{})
170169
err = creditorSwap.handleEmitChequeMsg(ctx, debitor, emitMsg)
171170
if err != nil {
172171
t.Fatal(err)
173172
}
174-
// ...on which we wait until the submitChequeAndCash is actually terminated (ensures proper nounce count)
173+
// ...on which we wait until the cashCheque is actually terminated (ensures proper nounce count)
175174
select {
176-
case <-testBackend.submitDone:
177-
log.Debug("submit and cash transactions completed and committed")
175+
case <-testBackend.cashDone:
176+
log.Debug("cash transaction completed and committed")
178177
case <-time.After(4 * time.Second):
179-
t.Fatalf("Timeout waiting for submit and cash transactions to complete")
178+
t.Fatalf("Timeout waiting for cash transaction to complete")
180179
}
181180
log.Debug("balance", "balance", creditorSwap.balances[debitor.ID()])
182181
// check that the balance has been reset
@@ -235,10 +234,23 @@ func TestTriggerPaymentThreshold(t *testing.T) {
235234
}
236235
cheque := debitorSwap.cheques[creditor.ID()]
237236
expectedAmount := uint64(overDraft) + DefaultPaymentThreshold
238-
if cheque.Amount != expectedAmount {
239-
t.Fatalf("Expected cheque amount to be %d, but is %d", expectedAmount, cheque.Amount)
237+
if cheque.CumulativePayout != expectedAmount {
238+
t.Fatalf("Expected cheque cumulative payout to be %d, but is %d", expectedAmount, cheque.CumulativePayout)
240239
}
241240

241+
// because no other accounting took place in the meantime the balance should be exactly 0
242+
if debitorSwap.balances[creditor.ID()] != 0 {
243+
t.Fatalf("Expected debitorSwap balance to be 0, but is %d", debitorSwap.balances[creditor.ID()])
244+
}
245+
246+
// do some accounting again to trigger a second cheque
247+
if err = debitorSwap.Add(int64(-DefaultPaymentThreshold), creditor.Peer); err != nil {
248+
t.Fatal(err)
249+
}
250+
251+
if debitorSwap.balances[creditor.ID()] != 0 {
252+
t.Fatalf("Expected debitorSwap balance to be 0, but is %d", debitorSwap.balances[creditor.ID()])
253+
}
242254
}
243255

244256
// TestTriggerDisconnectThreshold is to test that no further accounting takes place

0 commit comments

Comments
 (0)