diff --git a/client/asset/eth/eth.go b/client/asset/eth/eth.go index d5521ae817..3cd703802b 100644 --- a/client/asset/eth/eth.go +++ b/client/asset/eth/eth.go @@ -145,6 +145,7 @@ type ethFetcher interface { initiate(ctx context.Context, contracts []*asset.Contract, maxFeeRate uint64, contractVer uint32) (*types.Transaction, error) shutdown() syncProgress() ethereum.SyncProgress + isRedeemable(secretHash, secret [32]byte, contractVer uint32) (bool, error) redeem(ctx context.Context, redemptions []*asset.Redemption, maxFeeRate uint64, contractVer uint32) (*types.Transaction, error) refund(txOpts *bind.TransactOpts, secretHash [32]byte, contractVer uint32) (*types.Transaction, error) swap(ctx context.Context, secretHash [32]byte, contractVer uint32) (*dexeth.SwapState, error) @@ -684,11 +685,18 @@ func (eth *ExchangeWallet) Redeem(form *asset.RedeemForm) ([]dex.Bytes, asset.Co inputs := make([]dex.Bytes, 0, len(form.Redemptions)) var redeemedValue uint64 for _, redemption := range form.Redemptions { - var secretHash [32]byte + var secretHash, secret [32]byte copy(secretHash[:], redemption.Spends.SecretHash) - if secretHash != sha256.Sum256(redemption.Secret) { - return fail(fmt.Errorf("Redeem: secretHash %x != sha256(%x)", secretHash, redemption.Secret)) + copy(secret[:], redemption.Secret) + redeemable, err := eth.node.isRedeemable(secretHash, secret, form.AssetVersion) + if err != nil { + return fail(fmt.Errorf("Redeem: failed to check if swap is redeemable: %w", err)) + } + if !redeemable { + return fail(fmt.Errorf("Redeem: secretHash %x not redeemable with secret %x", + secretHash, secret)) } + swapData, err := eth.node.swap(eth.ctx, secretHash, form.AssetVersion) if err != nil { return nil, nil, 0, fmt.Errorf("Redeem: error finding swap state: %w", err) @@ -700,7 +708,7 @@ func (eth *ExchangeWallet) Redeem(form *asset.RedeemForm) ([]dex.Bytes, asset.Co fundsRequired := dexeth.RedeemGas(len(form.Redemptions), form.AssetVersion) * form.FeeSuggestion // TODO: make sure the amount we locked for redemption is enough to cover the gas - // fees. + // fees. Also unlock coins. _, err := eth.node.redeem(eth.ctx, form.Redemptions, form.FeeSuggestion, form.AssetVersion) if err != nil { return fail(fmt.Errorf("Redeem: redeem error: %w", err)) diff --git a/client/asset/eth/eth_test.go b/client/asset/eth/eth_test.go index 0cd1bf22ff..35d6b07972 100644 --- a/client/asset/eth/eth_test.go +++ b/client/asset/eth/eth_test.go @@ -63,6 +63,8 @@ type testNode struct { initErr error redeemErr error nonce uint64 + redeemable bool + isRedeemableErr error } func newBalance(current, pending, in, out uint64) *Balance { @@ -121,7 +123,9 @@ func (n *testNode) initiate(ctx context.Context, contracts []*asset.Contract, ma Nonce: n.nonce, }), nil } - +func (n *testNode) isRedeemable(secretHash [32]byte, secret [32]byte, contractVer uint32) (redeemable bool, err error) { + return n.redeemable, n.isRedeemableErr +} func (n *testNode) redeem(ctx context.Context, redemptions []*asset.Redemption, maxFeeRate uint64, contractVer uint32) (*types.Transaction, error) { if n.redeemErr != nil { return nil, n.redeemErr @@ -1172,14 +1176,17 @@ func TestRedeem(t *testing.T) { addSwapToSwapMap(secretHashes[1], 1e9, dexeth.SSInitiated) tests := []struct { - name string - form asset.RedeemForm - redeemErr error - expectError bool + name string + form asset.RedeemForm + redeemErr error + isRedeemable bool + isRedeemableErr error + expectError bool }{ { - name: "ok", - expectError: false, + name: "ok", + expectError: false, + isRedeemable: true, form: asset.RedeemForm{ Redemptions: []*asset.Redemption{ { @@ -1206,9 +1213,9 @@ func TestRedeem(t *testing.T) { }, }, { - name: "redeem error", - redeemErr: errors.New(""), - expectError: true, + name: "not redeemable", + expectError: true, + isRedeemable: false, form: asset.RedeemForm{ Redemptions: []*asset.Redemption{ { @@ -1220,24 +1227,44 @@ func TestRedeem(t *testing.T) { }, Secret: secrets[0][:], }, + { + Spends: &asset.AuditInfo{ + SecretHash: secretHashes[1][:], + Coin: &coin{ + id: encode.RandomBytes(32), + }, + }, + Secret: secrets[1][:], + }, }, - FeeSuggestion: 200, + FeeSuggestion: 100, AssetVersion: 0, }, }, { - name: "swap not found in contract", - expectError: true, + name: "isRedeemable error", + expectError: true, + isRedeemable: true, + isRedeemableErr: errors.New(""), form: asset.RedeemForm{ Redemptions: []*asset.Redemption{ { Spends: &asset.AuditInfo{ - SecretHash: secretHashes[2][:], + SecretHash: secretHashes[0][:], Coin: &coin{ id: encode.RandomBytes(32), }, }, - Secret: secrets[2][:], + Secret: secrets[0][:], + }, + { + Spends: &asset.AuditInfo{ + SecretHash: secretHashes[1][:], + Coin: &coin{ + id: encode.RandomBytes(32), + }, + }, + Secret: secrets[1][:], }, }, FeeSuggestion: 100, @@ -1245,13 +1272,15 @@ func TestRedeem(t *testing.T) { }, }, { - name: "hash of secret != secretHash", - expectError: true, + name: "redeem error", + redeemErr: errors.New(""), + isRedeemable: true, + expectError: true, form: asset.RedeemForm{ Redemptions: []*asset.Redemption{ { Spends: &asset.AuditInfo{ - SecretHash: secretHashes[1][:], + SecretHash: secretHashes[0][:], Coin: &coin{ id: encode.RandomBytes(32), }, @@ -1259,13 +1288,34 @@ func TestRedeem(t *testing.T) { Secret: secrets[0][:], }, }, + FeeSuggestion: 200, + AssetVersion: 0, + }, + }, + { + name: "swap not found in contract", + isRedeemable: true, + expectError: true, + form: asset.RedeemForm{ + Redemptions: []*asset.Redemption{ + { + Spends: &asset.AuditInfo{ + SecretHash: secretHashes[2][:], + Coin: &coin{ + id: encode.RandomBytes(32), + }, + }, + Secret: secrets[2][:], + }, + }, FeeSuggestion: 100, AssetVersion: 0, }, }, { - name: "empty redemptions slice error", - expectError: true, + name: "empty redemptions slice error", + isRedeemable: true, + expectError: true, form: asset.RedeemForm{ Redemptions: []*asset.Redemption{}, FeeSuggestion: 100, @@ -1276,6 +1326,8 @@ func TestRedeem(t *testing.T) { for _, test := range tests { node.redeemErr = test.redeemErr + node.redeemable = test.isRedeemable + node.isRedeemableErr = test.isRedeemableErr ins, out, fees, err := eth.Redeem(&test.form) if test.expectError {