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 }