Skip to content

Commit

Permalink
multi: handle trade suspension messages.
Browse files Browse the repository at this point in the history
This adds the suspension handler to the client core.
  • Loading branch information
dnldd committed Apr 17, 2020
1 parent 8d7d767 commit 512093c
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
38 changes: 37 additions & 1 deletion client/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type dexConnection struct {

tradeMtx sync.RWMutex
trades map[order.OrderID]*trackedTrade

suspensionMtx sync.RWMutex
suspensionEpoch uint64
}

// refreshMarkets rebuilds, saves, and returns the market map. The map itself
Expand Down Expand Up @@ -1085,6 +1088,24 @@ func (c *Core) Trade(pw string, form *TradeForm) (*Order, error) {
return nil, fmt.Errorf("unknown DEX %s", form.DEX)
}

dc.suspensionMtx.RLock()
suspensionEpoch := dc.suspensionEpoch
dc.suspensionMtx.RUnlock()

// TODO: (Refinement) instead of outrightly avoiding placing orders when
// a trade suspension is received, we need to decide whether a trade
// can be taken based on the block times of the blockchains involved
// in the trade.
//
// timeRemaining := time.Duration(int64(suspensionEpoch) - time.Now().Unix())

// Avoid placing an order if the connection is scheduled for a
// trade suspension.
if suspensionEpoch != 0 {
return nil, fmt.Errorf("order placement suspended due to impending"+
" trade suspension for dex %s", dc.acct.url)
}

rate, qty := form.Rate, form.Qty
if form.IsLimit && rate == 0 {
return nil, fmt.Errorf("zero-rate order not allowed")
Expand Down Expand Up @@ -1918,6 +1939,21 @@ func handleMatchProofMsg(_ *Core, dc *dexConnection, msg *msgjson.Message) error
return ob.ValidateMatchProof(note)
}

// handleTradeSuspensionMsg is called when a suspension message is received.
func handleTradeSuspensionMsg(_ *Core, dc *dexConnection, msg *msgjson.Message) error {
var suspension msgjson.TradeSuspensionRequest
err := msg.Unmarshal(&suspension)
if err != nil {
return fmt.Errorf("trade suspension unmarshal error: %v", err)
}

dc.suspensionMtx.Lock()
dc.suspensionEpoch = suspension.Epoch
dc.suspensionMtx.Unlock()

return nil
}

// routeHandler is a handler for a message from the DEX.
type routeHandler func(*Core, *dexConnection, *msgjson.Message) error

Expand All @@ -1928,7 +1964,7 @@ var reqHandlers = map[string]routeHandler{
msgjson.AuditRoute: handleAuditRoute,
msgjson.RedemptionRoute: handleRedemptionRoute,
msgjson.RevokeMatchRoute: nil,
msgjson.SuspensionRoute: nil,
msgjson.SuspensionRoute: handleTradeSuspensionMsg,
}

var noteHandlers = map[string]routeHandler{
Expand Down
38 changes: 38 additions & 0 deletions client/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2144,3 +2144,41 @@ func tMsgAudit(oid order.OrderID, mid order.MatchID, recipient string, val uint6
}
return audit, auditInfo
}

func TestHandleTradeSuspensionMsg(t *testing.T) {
rig := newTestRig()
payload := &msgjson.TradeSuspensionRequest{
Epoch: uint64(time.Now().Add(time.Hour * 24).Unix()),
Persist: true,
}

req, _ := msgjson.NewRequest(rig.dc.NextID(), msgjson.SuspensionRoute, payload)
err := handleTradeSuspensionMsg(rig.core, rig.dc, req)
if err != nil {
t.Fatalf("[handleTradeSuspensionMsg] unexpected error: %v", err)
}

rig.dc.suspensionMtx.RLock()
epochSet := rig.dc.suspensionEpoch != 0
rig.dc.suspensionMtx.RUnlock()

if !epochSet {
t.Fatal("expected the trade suspension starting epoch set")
}

form := &TradeForm{
DEX: tDexUrl,
IsLimit: true,
Sell: true,
Base: tDCR.ID,
Quote: tBTC.ID,
Qty: tDCR.LotSize * 10,
Rate: tBTC.RateStep * 1000,
TifNow: false,
}

_, err = rig.core.Trade(tPW, form)
if err == nil {
t.Fatalf("expected a trade suspension set error")
}
}
6 changes: 6 additions & 0 deletions dex/msgjson/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,12 @@ type PreimageRequest struct {
CommitChecksum Bytes `json:"csum"`
}

// TradeSuspensionRequest is the server-origination trade suspension payload.
type TradeSuspensionRequest struct {
Epoch uint64 `json:"epoch"`
Persist bool `json:"persist"`
}

// PreimageResponse is the client-originating preimage response payload.
type PreimageResponse struct {
Preimage Bytes `json:"pimg"`
Expand Down

0 comments on commit 512093c

Please sign in to comment.