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

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