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

     1  package v1
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/ethereum/go-ethereum/rlp"
    13  
    14  	"github.com/status-im/status-go/waku/common"
    15  )
    16  
    17  // statusOptionKey is a current type used in StatusOptions as a key.
    18  type statusOptionKey uint64
    19  
    20  var (
    21  	defaultMinPoW = math.Float64bits(0.001)
    22  	idxFieldKey   = make(map[int]statusOptionKey)
    23  	keyFieldIdx   = make(map[statusOptionKey]int)
    24  )
    25  
    26  // StatusOptions defines additional information shared between peers
    27  // during the handshake.
    28  // There might be more options provided then fields in StatusOptions
    29  // and they should be ignored during deserialization to stay forward compatible.
    30  // In the case of RLP, options should be serialized to an array of tuples
    31  // where the first item is a field name and the second is a RLP-serialized value.
    32  type StatusOptions struct {
    33  	PoWRequirement       *uint64            `rlp:"key=0"` // RLP does not support float64 natively
    34  	BloomFilter          []byte             `rlp:"key=1"`
    35  	LightNodeEnabled     *bool              `rlp:"key=2"`
    36  	ConfirmationsEnabled *bool              `rlp:"key=3"`
    37  	PacketRateLimits     *common.RateLimits `rlp:"key=4"`
    38  	TopicInterest        []common.TopicType `rlp:"key=5"`
    39  	BytesRateLimits      *common.RateLimits `rlp:"key=6"`
    40  }
    41  
    42  func StatusOptionsFromHost(host common.WakuHost) StatusOptions {
    43  	opts := StatusOptions{}
    44  
    45  	packetRateLimits := host.PacketRateLimits()
    46  	opts.PacketRateLimits = &packetRateLimits
    47  
    48  	bytesRateLimits := host.BytesRateLimits()
    49  	opts.BytesRateLimits = &bytesRateLimits
    50  
    51  	lightNode := host.LightClientMode()
    52  	opts.LightNodeEnabled = &lightNode
    53  
    54  	minPoW := host.MinPow()
    55  	opts.SetPoWRequirementFromF(minPoW)
    56  
    57  	confirmationsEnabled := host.ConfirmationsEnabled()
    58  	opts.ConfirmationsEnabled = &confirmationsEnabled
    59  
    60  	bloomFilterMode := host.BloomFilterMode()
    61  	if bloomFilterMode {
    62  		opts.BloomFilter = host.BloomFilter()
    63  	} else {
    64  		opts.TopicInterest = host.TopicInterest()
    65  	}
    66  
    67  	return opts
    68  }
    69  
    70  // initFLPKeyFields initialises the values of `idxFieldKey` and `keyFieldIdx`
    71  func initRLPKeyFields() {
    72  	o := StatusOptions{}
    73  	v := reflect.ValueOf(o)
    74  
    75  	for i := 0; i < v.NumField(); i++ {
    76  		// skip unexported fields
    77  		if !v.Field(i).CanInterface() {
    78  			continue
    79  		}
    80  		rlpTag := v.Type().Field(i).Tag.Get("rlp")
    81  
    82  		// skip fields without rlp field tag
    83  		if rlpTag == "" {
    84  			continue
    85  		}
    86  
    87  		keys := strings.Split(rlpTag, "=")
    88  
    89  		if len(keys) != 2 || keys[0] != "key" {
    90  			panic("invalid value of \"rlp\" tag, expected \"key=N\" where N is uint")
    91  		}
    92  		key, err := strconv.ParseUint(keys[1], 10, 64)
    93  		if err != nil {
    94  			panic("could not parse \"rlp\" key")
    95  		}
    96  
    97  		// typecast key to be of statusOptionKey type
    98  		keyFieldIdx[statusOptionKey(key)] = i
    99  		idxFieldKey[i] = statusOptionKey(key)
   100  	}
   101  }
   102  
   103  // WithDefaults adds the default values for a given peer.
   104  // This are not the host default values, but the default values that ought to
   105  // be used when receiving from an update from a peer.
   106  func (o StatusOptions) WithDefaults() StatusOptions {
   107  	if o.PoWRequirement == nil {
   108  		o.PoWRequirement = &defaultMinPoW
   109  	}
   110  
   111  	if o.LightNodeEnabled == nil {
   112  		lightNodeEnabled := false
   113  		o.LightNodeEnabled = &lightNodeEnabled
   114  	}
   115  
   116  	if o.ConfirmationsEnabled == nil {
   117  		confirmationsEnabled := false
   118  		o.ConfirmationsEnabled = &confirmationsEnabled
   119  	}
   120  
   121  	if o.PacketRateLimits == nil {
   122  		o.PacketRateLimits = &common.RateLimits{}
   123  	}
   124  
   125  	if o.BytesRateLimits == nil {
   126  		o.BytesRateLimits = &common.RateLimits{}
   127  	}
   128  
   129  	if o.BloomFilter == nil {
   130  		o.BloomFilter = common.MakeFullNodeBloom()
   131  	}
   132  
   133  	return o
   134  }
   135  
   136  func (o StatusOptions) PoWRequirementF() *float64 {
   137  	if o.PoWRequirement == nil {
   138  		return nil
   139  	}
   140  	result := math.Float64frombits(*o.PoWRequirement)
   141  	return &result
   142  }
   143  
   144  func (o *StatusOptions) SetPoWRequirementFromF(val float64) {
   145  	requirement := math.Float64bits(val)
   146  	o.PoWRequirement = &requirement
   147  }
   148  
   149  func (o StatusOptions) EncodeRLP(w io.Writer) error {
   150  	v := reflect.ValueOf(o)
   151  	var optionsList []interface{}
   152  	for i := 0; i < v.NumField(); i++ {
   153  		field := v.Field(i)
   154  		if !field.IsNil() {
   155  			value := field.Interface()
   156  			key, ok := idxFieldKey[i]
   157  			if !ok {
   158  				continue
   159  			}
   160  			if value != nil {
   161  				optionsList = append(optionsList, []interface{}{key, value})
   162  			}
   163  		}
   164  	}
   165  	return rlp.Encode(w, optionsList)
   166  }
   167  
   168  func (o *StatusOptions) DecodeRLP(s *rlp.Stream) error {
   169  	_, err := s.List()
   170  	if err != nil {
   171  		return fmt.Errorf("expected an outer list: %v", err)
   172  	}
   173  
   174  	v := reflect.ValueOf(o)
   175  
   176  loop:
   177  	for {
   178  		_, err := s.List()
   179  		switch err {
   180  		case nil:
   181  			// continue to decode a key
   182  		case rlp.EOL:
   183  			break loop
   184  		default:
   185  			return fmt.Errorf("expected an inner list: %v", err)
   186  		}
   187  		var key statusOptionKey
   188  		if err := s.Decode(&key); err != nil {
   189  			return fmt.Errorf("invalid key: %v", err)
   190  		}
   191  		// Skip processing if a key does not exist.
   192  		// It might happen when there is a new peer
   193  		// which supports a new option with
   194  		// a higher index.
   195  		idx, ok := keyFieldIdx[key]
   196  		if !ok {
   197  			// Read the rest of the list items and dump peer.
   198  			_, err := s.Raw()
   199  			if err != nil {
   200  				return fmt.Errorf("failed to read the value of key %d: %v", key, err)
   201  			}
   202  			continue
   203  		}
   204  		if err := s.Decode(v.Elem().Field(idx).Addr().Interface()); err != nil {
   205  			return fmt.Errorf("failed to decode an option %d: %v", key, err)
   206  		}
   207  		if err := s.ListEnd(); err != nil {
   208  			return err
   209  		}
   210  	}
   211  
   212  	return s.ListEnd()
   213  }
   214  
   215  func (o StatusOptions) Validate() error {
   216  	if len(o.TopicInterest) > 10000 {
   217  		return errors.New("topic interest is limited by 1000 items")
   218  	}
   219  	return nil
   220  }