github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/whisper/whisperv6/envelope.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  // Contains the Whisper protocol Envelope element.
    19  
    20  package whisperv6
    21  
    22  import (
    23  	"crypto/ecdsa"
    24  	"encoding/binary"
    25  	"fmt"
    26  	gmath "math"
    27  	"math/big"
    28  	"time"
    29  
    30  	"github.com/AigarNetwork/aigar/common"
    31  	"github.com/AigarNetwork/aigar/crypto"
    32  	"github.com/AigarNetwork/aigar/crypto/ecies"
    33  	"github.com/AigarNetwork/aigar/rlp"
    34  )
    35  
    36  // Envelope represents a clear-text data packet to transmit through the Whisper
    37  // network. Its contents may or may not be encrypted and signed.
    38  type Envelope struct {
    39  	Expiry uint32
    40  	TTL    uint32
    41  	Topic  TopicType
    42  	Data   []byte
    43  	Nonce  uint64
    44  
    45  	pow float64 // Message-specific PoW as described in the Whisper specification.
    46  
    47  	// the following variables should not be accessed directly, use the corresponding function instead: Hash(), Bloom()
    48  	hash  common.Hash // Cached hash of the envelope to avoid rehashing every time.
    49  	bloom []byte
    50  }
    51  
    52  // size returns the size of envelope as it is sent (i.e. public fields only)
    53  func (e *Envelope) size() int {
    54  	return EnvelopeHeaderLength + len(e.Data)
    55  }
    56  
    57  // rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
    58  func (e *Envelope) rlpWithoutNonce() []byte {
    59  	res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Data})
    60  	return res
    61  }
    62  
    63  // NewEnvelope wraps a Whisper message with expiration and destination data
    64  // included into an envelope for network forwarding.
    65  func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage) *Envelope {
    66  	env := Envelope{
    67  		Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
    68  		TTL:    ttl,
    69  		Topic:  topic,
    70  		Data:   msg.Raw,
    71  		Nonce:  0,
    72  	}
    73  
    74  	return &env
    75  }
    76  
    77  // Seal closes the envelope by spending the requested amount of time as a proof
    78  // of work on hashing the data.
    79  func (e *Envelope) Seal(options *MessageParams) error {
    80  	if options.PoW == 0 {
    81  		// PoW is not required
    82  		return nil
    83  	}
    84  
    85  	var target, bestLeadingZeros int
    86  	if options.PoW < 0 {
    87  		// target is not set - the function should run for a period
    88  		// of time specified in WorkTime param. Since we can predict
    89  		// the execution time, we can also adjust Expiry.
    90  		e.Expiry += options.WorkTime
    91  	} else {
    92  		target = e.powToFirstBit(options.PoW)
    93  	}
    94  
    95  	rlp := e.rlpWithoutNonce()
    96  	buf := make([]byte, len(rlp)+8)
    97  	copy(buf, rlp)
    98  	asAnInt := new(big.Int)
    99  
   100  	finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano()
   101  	for nonce := uint64(0); time.Now().UnixNano() < finish; {
   102  		for i := 0; i < 1024; i++ {
   103  			binary.BigEndian.PutUint64(buf[len(rlp):], nonce)
   104  			h := crypto.Keccak256(buf)
   105  			asAnInt.SetBytes(h)
   106  			leadingZeros := 256 - asAnInt.BitLen()
   107  			if leadingZeros > bestLeadingZeros {
   108  				e.Nonce, bestLeadingZeros = nonce, leadingZeros
   109  				if target > 0 && bestLeadingZeros >= target {
   110  					return nil
   111  				}
   112  			}
   113  			nonce++
   114  		}
   115  	}
   116  
   117  	if target > 0 && bestLeadingZeros < target {
   118  		return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime)
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  // PoW computes (if necessary) and returns the proof of work target
   125  // of the envelope.
   126  func (e *Envelope) PoW() float64 {
   127  	if e.pow == 0 {
   128  		e.calculatePoW(0)
   129  	}
   130  	return e.pow
   131  }
   132  
   133  func (e *Envelope) calculatePoW(diff uint32) {
   134  	rlp := e.rlpWithoutNonce()
   135  	buf := make([]byte, len(rlp)+8)
   136  	copy(buf, rlp)
   137  	binary.BigEndian.PutUint64(buf[len(rlp):], e.Nonce)
   138  	powHash := new(big.Int).SetBytes(crypto.Keccak256(buf))
   139  	leadingZeroes := 256 - powHash.BitLen()
   140  	x := gmath.Pow(2, float64(leadingZeroes))
   141  	x /= float64(len(rlp))
   142  	x /= float64(e.TTL + diff)
   143  	e.pow = x
   144  }
   145  
   146  func (e *Envelope) powToFirstBit(pow float64) int {
   147  	x := pow
   148  	x *= float64(e.size())
   149  	x *= float64(e.TTL)
   150  	bits := gmath.Log2(x)
   151  	bits = gmath.Ceil(bits)
   152  	res := int(bits)
   153  	if res < 1 {
   154  		res = 1
   155  	}
   156  	return res
   157  }
   158  
   159  // Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
   160  func (e *Envelope) Hash() common.Hash {
   161  	if (e.hash == common.Hash{}) {
   162  		encoded, _ := rlp.EncodeToBytes(e)
   163  		e.hash = crypto.Keccak256Hash(encoded)
   164  	}
   165  	return e.hash
   166  }
   167  
   168  // DecodeRLP decodes an Envelope from an RLP data stream.
   169  func (e *Envelope) DecodeRLP(s *rlp.Stream) error {
   170  	raw, err := s.Raw()
   171  	if err != nil {
   172  		return err
   173  	}
   174  	// The decoding of Envelope uses the struct fields but also needs
   175  	// to compute the hash of the whole RLP-encoded envelope. This
   176  	// type has the same structure as Envelope but is not an
   177  	// rlp.Decoder (does not implement DecodeRLP function).
   178  	// Only public members will be encoded.
   179  	type rlpenv Envelope
   180  	if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil {
   181  		return err
   182  	}
   183  	e.hash = crypto.Keccak256Hash(raw)
   184  	return nil
   185  }
   186  
   187  // OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
   188  func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) {
   189  	message := &ReceivedMessage{Raw: e.Data}
   190  	err := message.decryptAsymmetric(key)
   191  	switch err {
   192  	case nil:
   193  		return message, nil
   194  	case ecies.ErrInvalidPublicKey: // addressed to somebody else
   195  		return nil, err
   196  	default:
   197  		return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err)
   198  	}
   199  }
   200  
   201  // OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
   202  func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
   203  	msg = &ReceivedMessage{Raw: e.Data}
   204  	err = msg.decryptSymmetric(key)
   205  	if err != nil {
   206  		msg = nil
   207  	}
   208  	return msg, err
   209  }
   210  
   211  // Open tries to decrypt an envelope, and populates the message fields in case of success.
   212  func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
   213  	if watcher == nil {
   214  		return nil
   215  	}
   216  
   217  	// The API interface forbids filters doing both symmetric and asymmetric encryption.
   218  	if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() {
   219  		return nil
   220  	}
   221  
   222  	if watcher.expectsAsymmetricEncryption() {
   223  		msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
   224  		if msg != nil {
   225  			msg.Dst = &watcher.KeyAsym.PublicKey
   226  		}
   227  	} else if watcher.expectsSymmetricEncryption() {
   228  		msg, _ = e.OpenSymmetric(watcher.KeySym)
   229  		if msg != nil {
   230  			msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
   231  		}
   232  	}
   233  
   234  	if msg != nil {
   235  		ok := msg.ValidateAndParse()
   236  		if !ok {
   237  			return nil
   238  		}
   239  		msg.Topic = e.Topic
   240  		msg.PoW = e.PoW()
   241  		msg.TTL = e.TTL
   242  		msg.Sent = e.Expiry - e.TTL
   243  		msg.EnvelopeHash = e.Hash()
   244  	}
   245  	return msg
   246  }
   247  
   248  // Bloom maps 4-bytes Topic into 64-byte bloom filter with 3 bits set (at most).
   249  func (e *Envelope) Bloom() []byte {
   250  	if e.bloom == nil {
   251  		e.bloom = TopicToBloom(e.Topic)
   252  	}
   253  	return e.bloom
   254  }
   255  
   256  // TopicToBloom converts the topic (4 bytes) to the bloom filter (64 bytes)
   257  func TopicToBloom(topic TopicType) []byte {
   258  	b := make([]byte, BloomFilterSize)
   259  	var index [3]int
   260  	for j := 0; j < 3; j++ {
   261  		index[j] = int(topic[j])
   262  		if (topic[3] & (1 << uint(j))) != 0 {
   263  			index[j] += 256
   264  		}
   265  	}
   266  
   267  	for j := 0; j < 3; j++ {
   268  		byteIndex := index[j] / 8
   269  		bitIndex := index[j] % 8
   270  		b[byteIndex] = (1 << uint(bitIndex))
   271  	}
   272  	return b
   273  }
   274  
   275  // GetEnvelope retrieves an envelope from the message queue by its hash.
   276  // It returns nil if the envelope can not be found.
   277  func (w *Whisper) GetEnvelope(hash common.Hash) *Envelope {
   278  	w.poolMu.RLock()
   279  	defer w.poolMu.RUnlock()
   280  	return w.envelopes[hash]
   281  }