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 }