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 }