From 512093cc64cae14898457e6526f4effaa080cefe Mon Sep 17 00:00:00 2001 From: Donald Adu-Poku Date: Fri, 17 Apr 2020 18:33:53 +0000 Subject: [PATCH] multi: handle trade suspension messages. This adds the suspension handler to the client core. --- client/core/core.go | 38 +++++++++++++++++++++++++++++++++++++- client/core/core_test.go | 38 ++++++++++++++++++++++++++++++++++++++ dex/msgjson/types.go | 6 ++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/client/core/core.go b/client/core/core.go index a2b476bae5..0db8e0278f 100644 --- a/client/core/core.go +++ b/client/core/core.go @@ -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 @@ -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") @@ -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 @@ -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{ diff --git a/client/core/core_test.go b/client/core/core_test.go index 4e723331b2..8d2f892d53 100644 --- a/client/core/core_test.go +++ b/client/core/core_test.go @@ -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") + } +} diff --git a/dex/msgjson/types.go b/dex/msgjson/types.go index eedf5ea0aa..a6a5a123ac 100644 --- a/dex/msgjson/types.go +++ b/dex/msgjson/types.go @@ -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"`