github.com/status-im/status-go@v1.1.0/waku/mailserver.go (about)

     1  // Copyright 2019 The Waku Library Authors.
     2  //
     3  // The Waku library is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Lesser General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // (at your option) any later version.
     7  //
     8  // The Waku library is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty off
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    11  // GNU Lesser General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Lesser General Public License
    14  // along with the Waku library. If not, see <http://www.gnu.org/licenses/>.
    15  //
    16  // This software uses the go-ethereum library, which is licensed
    17  // under the GNU Lesser General Public Library, version 3 or any later.
    18  
    19  package waku
    20  
    21  import (
    22  	"bytes"
    23  	"errors"
    24  	"fmt"
    25  
    26  	"github.com/status-im/status-go/waku/common"
    27  
    28  	gethcommon "github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/p2p/enode"
    30  )
    31  
    32  const (
    33  	mailServerFailedPayloadPrefix = "ERROR="
    34  	cursorSize                    = 36
    35  )
    36  
    37  // MailServer represents a mail server, capable of
    38  // archiving the old messages for subsequent delivery
    39  // to the peers. Any implementation must ensure that both
    40  // functions are thread-safe. Also, they must return ASAP.
    41  // DeliverMail should use p2pMessageCode for delivery,
    42  // in order to bypass the expiry checks.
    43  type MailServer interface {
    44  	Archive(env *common.Envelope)
    45  	DeliverMail(peerID []byte, request *common.Envelope) // DEPRECATED; use Deliver()
    46  	Deliver(peerID []byte, request common.MessagesRequest)
    47  }
    48  
    49  // MailServerResponse is the response payload sent by the mailserver.
    50  type MailServerResponse struct {
    51  	LastEnvelopeHash gethcommon.Hash
    52  	Cursor           []byte
    53  	Error            error
    54  }
    55  
    56  func invalidResponseSizeError(size int) error {
    57  	return fmt.Errorf("unexpected payload size: %d", size)
    58  }
    59  
    60  // CreateMailServerRequestCompletedPayload creates a payload representing
    61  // a successful request to mailserver
    62  func CreateMailServerRequestCompletedPayload(requestID, lastEnvelopeHash gethcommon.Hash, cursor []byte) []byte {
    63  	payload := make([]byte, len(requestID))
    64  	copy(payload, requestID[:])
    65  	payload = append(payload, lastEnvelopeHash[:]...)
    66  	payload = append(payload, cursor...)
    67  	return payload
    68  }
    69  
    70  // CreateMailServerRequestFailedPayload creates a payload representing
    71  // a failed request to a mailserver
    72  func CreateMailServerRequestFailedPayload(requestID gethcommon.Hash, err error) []byte {
    73  	payload := []byte(mailServerFailedPayloadPrefix)
    74  	payload = append(payload, requestID[:]...)
    75  	payload = append(payload, []byte(err.Error())...)
    76  	return payload
    77  }
    78  
    79  // CreateMailServerEvent returns EnvelopeEvent with correct data
    80  // if payload corresponds to any of the know mailserver events:
    81  // * request completed successfully
    82  // * request failed
    83  // If the payload is unknown/unparseable, it returns `nil`
    84  func CreateMailServerEvent(nodeID enode.ID, payload []byte) (*common.EnvelopeEvent, error) {
    85  	if len(payload) < gethcommon.HashLength {
    86  		return nil, invalidResponseSizeError(len(payload))
    87  	}
    88  
    89  	event, err := tryCreateMailServerRequestFailedEvent(nodeID, payload)
    90  	if err != nil {
    91  		return nil, err
    92  	} else if event != nil {
    93  		return event, nil
    94  	}
    95  
    96  	return tryCreateMailServerRequestCompletedEvent(nodeID, payload)
    97  }
    98  
    99  func tryCreateMailServerRequestFailedEvent(nodeID enode.ID, payload []byte) (*common.EnvelopeEvent, error) {
   100  	if len(payload) < gethcommon.HashLength+len(mailServerFailedPayloadPrefix) {
   101  		return nil, nil
   102  	}
   103  
   104  	prefix, remainder := extractPrefix(payload, len(mailServerFailedPayloadPrefix))
   105  
   106  	if !bytes.Equal(prefix, []byte(mailServerFailedPayloadPrefix)) {
   107  		return nil, nil
   108  	}
   109  
   110  	var (
   111  		requestID gethcommon.Hash
   112  		errorMsg  string
   113  	)
   114  
   115  	requestID, remainder = extractHash(remainder)
   116  	errorMsg = string(remainder)
   117  
   118  	event := common.EnvelopeEvent{
   119  		Peer:  nodeID,
   120  		Hash:  requestID,
   121  		Event: common.EventMailServerRequestCompleted,
   122  		Data: &MailServerResponse{
   123  			Error: errors.New(errorMsg),
   124  		},
   125  	}
   126  
   127  	return &event, nil
   128  
   129  }
   130  
   131  func tryCreateMailServerRequestCompletedEvent(nodeID enode.ID, payload []byte) (*common.EnvelopeEvent, error) {
   132  	// check if payload is
   133  	// - requestID or
   134  	// - requestID + lastEnvelopeHash or
   135  	// - requestID + lastEnvelopeHash + cursor
   136  	// requestID is the hash of the request envelope.
   137  	// lastEnvelopeHash is the last envelope sent by the mail server
   138  	// cursor is the db key, 36 bytes: 4 for the timestamp + 32 for the envelope hash.
   139  	if len(payload) > gethcommon.HashLength*2+cursorSize {
   140  		return nil, invalidResponseSizeError(len(payload))
   141  	}
   142  
   143  	var (
   144  		requestID        gethcommon.Hash
   145  		lastEnvelopeHash gethcommon.Hash
   146  		cursor           []byte
   147  	)
   148  
   149  	requestID, remainder := extractHash(payload)
   150  
   151  	if len(remainder) >= gethcommon.HashLength {
   152  		lastEnvelopeHash, remainder = extractHash(remainder)
   153  	}
   154  
   155  	if len(remainder) >= cursorSize {
   156  		cursor = remainder
   157  	}
   158  
   159  	event := common.EnvelopeEvent{
   160  		Peer:  nodeID,
   161  		Hash:  requestID,
   162  		Event: common.EventMailServerRequestCompleted,
   163  		Data: &MailServerResponse{
   164  			LastEnvelopeHash: lastEnvelopeHash,
   165  			Cursor:           cursor,
   166  		},
   167  	}
   168  
   169  	return &event, nil
   170  }
   171  
   172  func extractHash(payload []byte) (gethcommon.Hash, []byte) {
   173  	prefix, remainder := extractPrefix(payload, gethcommon.HashLength)
   174  	return gethcommon.BytesToHash(prefix), remainder
   175  }
   176  
   177  func extractPrefix(payload []byte, size int) ([]byte, []byte) {
   178  	return payload[:size], payload[size:]
   179  }