Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Commit

Permalink
[#1069] Analyze/send ORDER messages on ORDER_PROCESSING_ERROR
Browse files Browse the repository at this point in the history
  • Loading branch information
placer14 committed Aug 2, 2019
1 parent 8a0a3e2 commit da9a4d8
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 38 deletions.
40 changes: 2 additions & 38 deletions api/jsonapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -4240,47 +4240,11 @@ func (i *jsonAPIHandler) POSTResendOrderMessage(w http.ResponseWriter, r *http.R
return
}

if args.OrderID == "" {
ErrorResponse(w, http.StatusBadRequest, core.ErrOrderNotFound.Error())
return
}

var msgType int32
var ok bool

if msgType, ok = pb.Message_MessageType_value[args.MessageType]; !ok {
ErrorResponse(w, http.StatusBadRequest, "invalid order message type")
return
}

msg, peerID, err := i.node.Datastore.Messages().
GetByOrderIDType(args.OrderID, pb.Message_MessageType(msgType))
if err != nil || msg == nil || msg.Msg.GetPayload() == nil {
ErrorResponse(w, http.StatusBadRequest, "order message not found")
return
}

p, err := peer.IDB58Decode(peerID)
if err != nil {
ErrorResponse(w, http.StatusBadRequest, "invalid peer id")
if err := i.node.ResendCachedOrderMessage(args.OrderID, args.MessageType); err != nil {
ErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

err = i.node.Service.SendMessage(ctx, p, &msg.Msg)
if err != nil {
// If send message failed, try sending offline message
log.Warningf("resending message failed: %v", err)
err = i.node.SendOfflineMessage(p, nil, &msg.Msg)
if err != nil {
log.Errorf("resending offline message failed: %v", err)
ErrorResponse(w, http.StatusBadRequest, "order message not sent")
return
}
}

SanitizedResponse(w, `{}`)
}

Expand Down
28 changes: 28 additions & 0 deletions core/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,34 @@ func (n *OpenBazaarNode) Unfollow(peerID string) error {
return nil
}

func (n *OpenBazaarNode) ResendCachedOrderMessage(orderID string, msgType pb.Message_MessageType) error {
if _, ok := pb.Message_MessageType_name[int32(msgType)]; !ok {
return fmt.Errorf("invalid order message type (%d)", int(msgType))
}

msg, peerID, err := n.Datastore.Messages().GetByOrderIDType(orderID, msgType)
if err != nil || msg == nil || msg.Msg.GetPayload() == nil {
return fmt.Errorf("unable to find message for order ID (%s) and message type (%s)", orderID, msgType.String())
}

p, err := peer.IDB58Decode(peerID)
if err != nil {
return fmt.Errorf("unable to decode invalid peer ID for order (%s) and message type (%s)", orderID, msgType.String())
}

ctx, cancel := context.WithTimeout(context.Background(), n.OfflineMessageFailoverTimeout)
defer cancel()

if err = n.Service.SendMessage(ctx, p, &msg.Msg); err != nil {
go func() {
if err := n.SendOfflineMessage(p, nil, &msg.Msg); err != nil {
log.Errorf("error resending offline message for order id (%s) and message type (%+v): %s", orderID, msgType, err.Error())
}
}()
}
return nil
}

// SendOrder - send order created msg to peer
func (n *OpenBazaarNode) SendOrder(peerID string, contract *pb.RicardianContract) (resp *pb.Message, err error) {
p, err := peer.IDB58Decode(peerID)
Expand Down
85 changes: 85 additions & 0 deletions net/service/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ func (service *OpenBazaarService) HandlerForMsgType(t pb.Message_MessageType) fu
return service.handleStore
case pb.Message_ERROR:
return service.handleError
case pb.Message_ORDER_PROCESSING_FAILURE:
return service.handleOrderProcessingFailure
default:
return nil
}
Expand Down Expand Up @@ -1232,6 +1234,89 @@ func (service *OpenBazaarService) handleDisputeClose(p peer.ID, pmes *pb.Message
return nil, nil
}

func (service *OpenBazaarService) handleOrderProcessingFailure(p peer.ID, pmes *pb.Message, options interface{}) (*pb.Message, error) {
if pmes.Payload == nil {
return nil, ErrEmptyPayload
}
procFailure := new(pb.OrderProcessingFailure)
if err := ptypes.UnmarshalAny(pmes.Payload, procFailure); err != nil {
return nil, err
}

var (
localContract *pb.RicardianContract
)
if contract, _, _, _, _, _, err := service.node.Datastore.Sales().GetByOrderId(procFailure.OrderID); err != nil {
localContract = contract
} else if contract, _, _, _, _, _, err := service.node.Datastore.Purchases().GetByOrderId(procFailure.OrderID); err != nil {
localContract = contract
}
if localContract == nil {
return nil, fmt.Errorf("no contract found for order ID (%s)", procFailure.OrderID)
}
// TODO: Validate we aren't in a loop, exit if we are
missingMessageTypes, err := analyzeForMissingMessages(localContract, procFailure)
if err != nil {
return nil, err
}
if missingMessageTypes == nil {
err := fmt.Errorf("unable to determine missing message types for order ID (%s)", procFailure.OrderID)
log.Error(err.Error())
return nil, err
}
for _, msgType := range missingMessageTypes {
log.Debugf("resending missing ORDER message (%s) to peer (%s)", msgType.String(), p.Pretty())
if err := service.node.ResendCachedOrderMessage(procFailure.OrderID, msgType); err != nil {
err := fmt.Errorf("resending message type (%s) for order (%s): %s", msgType.String(), procFailure.OrderID)
log.Error(err.Error())
// TODO: Can we attempt to recreate MessageType?
return nil, err
}
}
return nil, nil
}

// analyzeForMissingMessages compares the local RicardianContract with the one provided with the
// error message to determine which messages might be missing and need to be resent to the remote
// peer
func analyzeForMissingMessages(lc *pb.RicardianContract, e *pb.OrderProcessingFailure) ([]pb.Message_MessageType, error) {
var msgsToResend = []pb.Message_MessageType{}
if lc == nil {
return nil, fmt.Errorf("no local contract for order ID (%s) to compare to, unable to recover missing messages", e.OrderID)
}
if lc.BuyerOrder != nil && (e.Contract == nil || e.Contract.BuyerOrder == nil) {
msgsToResend = append(msgsToResend, pb.Message_ORDER)
}
if lc.VendorOrderConfirmation != nil && (e.Contract == nil || e.Contract.VendorOrderConfirmation == nil) {
msgsToResend = append(msgsToResend, pb.Message_ORDER_CONFIRMATION)
}
// TODO: How do we detect ORDER_REJECT diff?
// TODO: How do we detect ORDER_CANCEL diff?
if len(lc.VendorOrderFulfillment) != 0 && (e.Contract == nil || len(e.Contract.VendorOrderFulfillment) == 0) {
msgsToResend = append(msgsToResend, pb.Message_ORDER_FULFILLMENT)
}
if lc.Dispute != nil && (e.Contract == nil || e.Contract.Dispute == nil) {
msgsToResend = append(msgsToResend, pb.Message_DISPUTE_OPEN)
msgsToResend = append(msgsToResend, pb.Message_DISPUTE_UPDATE)
}
if lc.DisputeResolution != nil && (e.Contract == nil || e.Contract.DisputeResolution == nil) {
// TODO: Re-broadcast error to moderator so they can resend DISPUTE_CLOSE
msgsToResend = append(msgsToResend, pb.Message_DISPUTE_CLOSE)
}
if lc.DisputeAcceptance != nil && (e.Contract == nil || e.Contract.DisputeAcceptance == nil) {
// TODO: This msg occurs after order is PENDING, FULFILLED, and DISPUTED states, is
// there a better check for resending FINALIZED_PAYMENT?
msgsToResend = append(msgsToResend, pb.Message_VENDOR_FINALIZED_PAYMENT)
}
if lc.Refund != nil && (e.Contract == nil || e.Contract.Refund == nil) {
msgsToResend = append(msgsToResend, pb.Message_REFUND)
}
if lc.BuyerOrderCompletion != nil && (e.Contract == nil || e.Contract.BuyerOrderCompletion == nil) {
msgsToResend = append(msgsToResend, pb.Message_ORDER_COMPLETION)
}
return msgsToResend, nil
}

func (service *OpenBazaarService) handleChat(p peer.ID, pmes *pb.Message, options interface{}) (*pb.Message, error) {

// Unmarshall
Expand Down

0 comments on commit da9a4d8

Please sign in to comment.