Skip to content

Commit

Permalink
client/core: ensure redeem is accepted before confirm/complete match
Browse files Browse the repository at this point in the history
  • Loading branch information
chappjc committed Jul 12, 2023
1 parent 49361da commit ae28c2d
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 20 deletions.
21 changes: 13 additions & 8 deletions client/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8723,7 +8723,7 @@ func TestConfirmRedemption(t *testing.T) {

tBtcWallet.redeemCoins = []dex.Bytes{tUpdatedCoinID}

setupMatch := func(status order.MatchStatus) {
setupMatch := func(status order.MatchStatus, side order.MatchSide) {
matchID := ordertest.RandomMatchID()
_, auditInfo := tMsgAudit(oid, matchID, addr, 0, secretHash[:])
matchTime := time.Now()
Expand All @@ -8734,21 +8734,27 @@ func TestConfirmRedemption(t *testing.T) {
UserMatch: &order.UserMatch{
MatchID: matchID,
Address: addr,
Side: side,
Status: status,
},
},
}
tracker.matches = map[order.MatchID]*matchTracker{matchID: match}

isMaker := match.Side == order.Maker
match.Status = status
match.MetaData.Proof = db.MatchProof{}
proof := &match.MetaData.Proof
proof.Auth.InitSig = []byte{1, 2, 3, 4}

// Assume our redeem was accepted, if we sent one.
if isMaker {
auditInfo.Expiration = matchTime.Add(tracker.lockTimeTaker)
if status >= order.MakerRedeemed {
match.MetaData.Proof.Auth.RedeemSig = []byte{0}
}
} else {
auditInfo.Expiration = matchTime.Add(tracker.lockTimeMaker)
if status >= order.MatchComplete {
match.MetaData.Proof.Auth.RedeemSig = []byte{0}
}
}

if status >= order.MakerSwapCast {
Expand Down Expand Up @@ -8990,8 +8996,7 @@ func TestConfirmRedemption(t *testing.T) {

for _, test := range tests {
tracker.mtx.Lock()
setupMatch(test.matchStatus)
match.Side = test.matchSide
setupMatch(test.matchStatus, test.matchSide)
tracker.mtx.Unlock()

tBtcWallet.confirmRedemptionResult = test.confirmRedemptionResult
Expand All @@ -9001,8 +9006,8 @@ func TestConfirmRedemption(t *testing.T) {
tCore.tickAsset(dc, tUTXOAssetB.ID)

if tBtcWallet.confirmRedemptionCalled != test.expectConfirmRedemptionCalled {
t.Fatalf("%s: expected confirm redemption to be called %v but got %v",
test.name, tBtcWallet.confirmRedemptionCalled, test.expectConfirmRedemptionCalled)
t.Fatalf("%s: expected confirm redemption to be called=%v but got=%v",
test.name, test.expectConfirmRedemptionCalled, tBtcWallet.confirmRedemptionCalled)
}

for _, expectedNotification := range test.expectedNotifications {
Expand Down
44 changes: 32 additions & 12 deletions client/core/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ type matchTracker struct {

// confirmRedemptionNumTries is just used for logging.
confirmRedemptionNumTries int
// redemptionConfs and redemptionConfsReq are set while the redemption
// confirmation process is running.
// redemptionConfs and redemptionConfsReq are updated while the redemption
// confirmation process is running. Their values are not updated after the
// match reaches MatchConfirmed status.
redemptionConfs uint64
redemptionConfsReq uint64

Expand Down Expand Up @@ -2096,7 +2097,7 @@ func (c *Core) resendPendingRequests(t *trackedTrade) {
swapCoinID = proof.MakerSwap
case side == order.Taker && status == order.TakerSwapCast:
swapCoinID = proof.TakerSwap
case side == order.Maker && status == order.MakerRedeemed:
case side == order.Maker && status >= order.MakerRedeemed:
redeemCoinID = proof.MakerRedeem
case side == order.Taker && status >= order.MatchComplete:
redeemCoinID = proof.TakerRedeem
Expand Down Expand Up @@ -2834,12 +2835,22 @@ func (c *Core) sendRedeemAsync(t *trackedTrade, match *matchTracker, coinID, sec
if match.Side == order.Maker && match.Status < order.MatchComplete {
// As maker, this is the end. However, this diverges from server,
// which still needs taker's redeem.
match.Status = order.MatchComplete
_, isConfirmer := t.wallets.toWallet.Wallet.(asset.RedemptionConfirmer)
if !isConfirmer || (match.redemptionConfs > 0 && match.redemptionConfs >= match.redemptionConfsReq) {
match.Status = order.MatchConfirmed // not a confirmer or redeem tx already confirmed before redeem request accepted by server
} else {
match.Status = order.MatchComplete
}
}
err = t.db.UpdateMatch(&match.MetaMatch)
if err != nil {
err = fmt.Errorf("error storing redeem ack sig in database: %v", err)
}
if match.Status == order.MatchConfirmed {
subject, details := t.formatDetails(TopicRedemptionConfirmed, match.token(), t.token())
note := newMatchNote(TopicRedemptionConfirmed, subject, details, db.Success, t, match)
t.notify(note)
}
t.mtx.Unlock()
}()
}
Expand All @@ -2851,14 +2862,25 @@ func (c *Core) sendRedeemAsync(t *trackedTrade, match *matchTracker, coinID, sec
// mutex lock held for writes.
func (t *trackedTrade) confirmRedemption(match *matchTracker) {
confirmer, isConfirmer := t.wallets.toWallet.Wallet.(asset.RedemptionConfirmer)
if !isConfirmer {
if !isConfirmer || (match.redemptionConfs > 0 && match.redemptionConfs >= match.redemptionConfsReq) { // already there, stop checking
if len(match.MetaData.Proof.Auth.RedeemSig) == 0 && !t.isSelfGoverned() {
return // waiting on redeem request to succeed
}
// Redeem request just succeeded or we gave up on the server.
if match.Status == order.MatchConfirmed {
return // raced with concurrent sendRedeemAsync
}
match.Status = order.MatchConfirmed
err := t.db.UpdateMatch(&match.MetaMatch)
if err != nil {
t.dc.log.Errorf("Failed to update match in db %v", err)
t.dc.log.Errorf("failed to update match in db: %v", err)
}
subject, details := t.formatDetails(TopicRedemptionConfirmed, match.token(), t.token())
note := newMatchNote(TopicRedemptionConfirmed, subject, details, db.Success, t, match)
t.notify(note)
return
}

if !t.wallets.toWallet.connected() {
return
}
Expand Down Expand Up @@ -2903,14 +2925,12 @@ func (t *trackedTrade) confirmRedemption(match *matchTracker) {
}
}

if redemptionStatus.Req <= redemptionStatus.Confs {
match.redemptionConfs, match.redemptionConfsReq = redemptionStatus.Confs, redemptionStatus.Req

if redemptionStatus.Confs >= redemptionStatus.Req &&
(len(match.MetaData.Proof.Auth.RedeemSig) > 0 || t.isSelfGoverned()) {
redemptionConfirmed = true
match.Status = order.MatchConfirmed
match.redemptionConfs = 0
match.redemptionConfsReq = 0
} else {
match.redemptionConfs = redemptionStatus.Confs
match.redemptionConfsReq = redemptionStatus.Req
}

if redemptionResubmitted || redemptionConfirmed {
Expand Down

0 comments on commit ae28c2d

Please sign in to comment.