github.com/bcskill/bcschain/v3@v3.4.9-beta2/whisper/mailserver/mailserver.go (about)

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