github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/whisper/mailserver/mailserver.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package mailserver provides a naive, example mailserver implementation 19 package mailserver 20 21 import ( 22 "encoding/binary" 23 "fmt" 24 25 "github.com/AigarNetwork/aigar/common" 26 "github.com/AigarNetwork/aigar/crypto" 27 "github.com/AigarNetwork/aigar/log" 28 "github.com/AigarNetwork/aigar/rlp" 29 whisper "github.com/AigarNetwork/aigar/whisper/whisperv6" 30 "github.com/syndtr/goleveldb/leveldb" 31 "github.com/syndtr/goleveldb/leveldb/opt" 32 "github.com/syndtr/goleveldb/leveldb/util" 33 ) 34 35 // WMailServer represents the state data of the mailserver. 36 type WMailServer struct { 37 db *leveldb.DB 38 w *whisper.Whisper 39 pow float64 40 key []byte 41 } 42 43 type DBKey struct { 44 timestamp uint32 45 hash common.Hash 46 raw []byte 47 } 48 49 // NewDbKey is a helper function that creates a levelDB 50 // key from a hash and an integer. 51 func NewDbKey(t uint32, h common.Hash) *DBKey { 52 const sz = common.HashLength + 4 53 var k DBKey 54 k.timestamp = t 55 k.hash = h 56 k.raw = make([]byte, sz) 57 binary.BigEndian.PutUint32(k.raw, k.timestamp) 58 copy(k.raw[4:], k.hash[:]) 59 return &k 60 } 61 62 // Init initializes the mail server. 63 func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) error { 64 var err error 65 if len(path) == 0 { 66 return fmt.Errorf("DB file is not specified") 67 } 68 69 if len(password) == 0 { 70 return fmt.Errorf("password is not specified") 71 } 72 73 s.db, err = leveldb.OpenFile(path, &opt.Options{OpenFilesCacheCapacity: 32}) 74 if err != nil { 75 return fmt.Errorf("open DB file: %s", err) 76 } 77 78 s.w = shh 79 s.pow = pow 80 81 MailServerKeyID, err := s.w.AddSymKeyFromPassword(password) 82 if err != nil { 83 return fmt.Errorf("create symmetric key: %s", err) 84 } 85 s.key, err = s.w.GetSymKey(MailServerKeyID) 86 if err != nil { 87 return fmt.Errorf("save symmetric key: %s", err) 88 } 89 return nil 90 } 91 92 // Close cleans up before shutdown. 93 func (s *WMailServer) Close() { 94 if s.db != nil { 95 s.db.Close() 96 } 97 } 98 99 // Archive stores the 100 func (s *WMailServer) Archive(env *whisper.Envelope) { 101 key := NewDbKey(env.Expiry-env.TTL, env.Hash()) 102 rawEnvelope, err := rlp.EncodeToBytes(env) 103 if err != nil { 104 log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err)) 105 } else { 106 err = s.db.Put(key.raw, rawEnvelope, nil) 107 if err != nil { 108 log.Error(fmt.Sprintf("Writing to DB failed: %s", err)) 109 } 110 } 111 } 112 113 // DeliverMail responds with saved messages upon request by the 114 // messages' owner. 115 func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) { 116 if peer == nil { 117 log.Error("Whisper peer is nil") 118 return 119 } 120 121 ok, lower, upper, bloom := s.validateRequest(peer.ID(), request) 122 if ok { 123 s.processRequest(peer, lower, upper, bloom) 124 } 125 } 126 127 func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bloom []byte) []*whisper.Envelope { 128 ret := make([]*whisper.Envelope, 0) 129 var err error 130 var zero common.Hash 131 kl := NewDbKey(lower, zero) 132 ku := NewDbKey(upper+1, zero) // LevelDB is exclusive, while the Whisper API is inclusive 133 i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil) 134 defer i.Release() 135 136 for i.Next() { 137 var envelope whisper.Envelope 138 err = rlp.DecodeBytes(i.Value(), &envelope) 139 if err != nil { 140 log.Error(fmt.Sprintf("RLP decoding failed: %s", err)) 141 } 142 143 if whisper.BloomFilterMatch(bloom, envelope.Bloom()) { 144 if peer == nil { 145 // used for test purposes 146 ret = append(ret, &envelope) 147 } else { 148 err = s.w.SendP2PDirect(peer, &envelope) 149 if err != nil { 150 log.Error(fmt.Sprintf("Failed to send direct message to peer: %s", err)) 151 return nil 152 } 153 } 154 } 155 } 156 157 err = i.Error() 158 if err != nil { 159 log.Error(fmt.Sprintf("Level DB iterator error: %s", err)) 160 } 161 162 return ret 163 } 164 165 func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, []byte) { 166 if s.pow > 0.0 && request.PoW() < s.pow { 167 return false, 0, 0, nil 168 } 169 170 f := whisper.Filter{KeySym: s.key} 171 decrypted := request.Open(&f) 172 if decrypted == nil { 173 log.Warn(fmt.Sprintf("Failed to decrypt p2p request")) 174 return false, 0, 0, nil 175 } 176 177 src := crypto.FromECDSAPub(decrypted.Src) 178 if len(src)-len(peerID) == 1 { 179 src = src[1:] 180 } 181 182 // if you want to check the signature, you can do it here. e.g.: 183 // if !bytes.Equal(peerID, src) { 184 if src == nil { 185 log.Warn(fmt.Sprintf("Wrong signature of p2p request")) 186 return false, 0, 0, nil 187 } 188 189 var bloom []byte 190 payloadSize := len(decrypted.Payload) 191 if payloadSize < 8 { 192 log.Warn(fmt.Sprintf("Undersized p2p request")) 193 return false, 0, 0, nil 194 } else if payloadSize == 8 { 195 bloom = whisper.MakeFullNodeBloom() 196 } else if payloadSize < 8+whisper.BloomFilterSize { 197 log.Warn(fmt.Sprintf("Undersized bloom filter in p2p request")) 198 return false, 0, 0, nil 199 } else { 200 bloom = decrypted.Payload[8 : 8+whisper.BloomFilterSize] 201 } 202 203 lower := binary.BigEndian.Uint32(decrypted.Payload[:4]) 204 upper := binary.BigEndian.Uint32(decrypted.Payload[4:8]) 205 return true, lower, upper, bloom 206 }