Refactor to waku handshake keyType identification

Presuming that all RLP keys will be of the same type. Still failing on forward and backward compatibility, so I will fidn a solution for this next
This commit is contained in:
Samuel Hawksby-Robinson 2020-04-09 23:41:10 +01:00 committed by Andrea Maria Piana
parent c16eba55f6
commit 042ae30f9f
2 changed files with 34 additions and 74 deletions

View File

@ -18,12 +18,6 @@ type statusOptionKey uint
// statusOptionKeyType is a type of a statusOptions key used for a particular instance of statusOptions struct. // statusOptionKeyType is a type of a statusOptions key used for a particular instance of statusOptions struct.
type statusOptionKeyType uint type statusOptionKeyType uint
type statusOptionKeyToType struct {
Idx int
Key statusOptionKey
Type statusOptionKeyType
}
const ( const (
sOKTS statusOptionKeyType = iota + 1 // Status Option Key Type String sOKTS statusOptionKeyType = iota + 1 // Status Option Key Type String
sOKTU // Status Option Key Type Uint sOKTU // Status Option Key Type Uint
@ -35,11 +29,6 @@ var (
keyFieldIdx = make(map[statusOptionKey]int) keyFieldIdx = make(map[statusOptionKey]int)
) )
type keyTypeMapping struct {
idxFieldKey map[int]*statusOptionKeyToType
keyFieldIdx map[statusOptionKey]*statusOptionKeyToType
}
// statusOptions defines additional information shared between peers // statusOptions defines additional information shared between peers
// during the handshake. // during the handshake.
// There might be more options provided then fields in statusOptions // There might be more options provided then fields in statusOptions
@ -53,7 +42,7 @@ type statusOptions struct {
ConfirmationsEnabled *bool `rlp:"key=3"` ConfirmationsEnabled *bool `rlp:"key=3"`
RateLimits *RateLimits `rlp:"key=4"` RateLimits *RateLimits `rlp:"key=4"`
TopicInterest []TopicType `rlp:"key=5"` TopicInterest []TopicType `rlp:"key=5"`
keyTypeMapping keyTypeMapping keyType statusOptionKeyType
} }
// initFLPKeyFields initialises the values of `idxFieldKey` and `keyFieldIdx` // initFLPKeyFields initialises the values of `idxFieldKey` and `keyFieldIdx`
@ -74,10 +63,14 @@ func initRLPKeyFields() error {
keys := strings.Split(rlpTag, "=") keys := strings.Split(rlpTag, "=")
// parse keys[1] as an int if len(keys) != 2 || keys[0] != "key" {
panic("invalid value of \"rlp\" tag, expected \"key=N\" where N is uint")
}
// parse keys[1] as an uint
key, err := strconv.ParseUint(keys[1], 10, 64) key, err := strconv.ParseUint(keys[1], 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("malformed rlp tag '%s', tag should be formatted 'rlp:\"key=0\"': %v", rlpTag, err) return fmt.Errorf("malformed rlp tag '%s', expected \"key=N\" where N is uint: %v", rlpTag, err)
} }
// typecast key to be of statusOptionKey type // typecast key to be of statusOptionKey type
@ -151,25 +144,8 @@ func (o statusOptions) EncodeRLP(w io.Writer) error {
continue continue
} }
var val []interface{}
if o.keyTypeMapping.idxFieldKey == nil {
val = append(val, key, value)
}
k, ok := o.keyTypeMapping.idxFieldKey[i]
if !ok {
val = append(val, key, value)
} else {
ke, err := k.encode()
if err != nil {
return fmt.Errorf("key encoding fail: %v", err)
}
val = append(val, ke, value)
}
if value != nil { if value != nil {
optionsList = append(optionsList, val) optionsList = append(optionsList, []interface{}{o.encodeKey(key), value})
} }
} }
return rlp.Encode(w, optionsList) return rlp.Encode(w, optionsList)
@ -194,30 +170,25 @@ loop:
return fmt.Errorf("expected an inner list: %v", err) return fmt.Errorf("expected an inner list: %v", err)
} }
ktt := statusOptionKeyToType{} key, keyType, err := o.decodeKey(s)
if err = ktt.decodeStream(s); err != nil { o.setKeyType(keyType)
return fmt.Errorf("invalid key: %v", err)
}
// Skip processing if a key does not exist. // Skip processing if a key does not exist.
// It might happen when there is a new peer // It might happen when there is a new peer
// which supports a new option with // which supports a new option with
// a higher index. // a higher index.
idx, ok := keyFieldIdx[ktt.Key] idx, ok := keyFieldIdx[key]
if !ok { if !ok {
// Read the rest of the list items and dump them. // Read the rest of the list items and dump them.
_, err := s.Raw() _, err := s.Raw()
if err != nil { if err != nil {
return fmt.Errorf("failed to read the value of key %d: %v", ktt.Key, err) return fmt.Errorf("failed to read the value of key %d: %v", key, err)
} }
continue continue
} }
ktt.Idx = idx
o.addKeyToType(&ktt)
if err := s.Decode(v.Elem().Field(idx).Addr().Interface()); err != nil { if err := s.Decode(v.Elem().Field(idx).Addr().Interface()); err != nil {
return fmt.Errorf("failed to decode an option %d: %v", ktt.Key, err) return fmt.Errorf("failed to decode an option %d: %v", key, err)
} }
if err := s.ListEnd(); err != nil { if err := s.ListEnd(); err != nil {
return err return err
@ -227,58 +198,46 @@ loop:
return s.ListEnd() return s.ListEnd()
} }
func (o *statusOptions) addKeyToType(ktt *statusOptionKeyToType) { func (o statusOptions) decodeKey(s *rlp.Stream) (statusOptionKey, statusOptionKeyType, error) {
if o.keyTypeMapping.idxFieldKey == nil {
o.keyTypeMapping.idxFieldKey = make(map[int]*statusOptionKeyToType)
}
if o.keyTypeMapping.keyFieldIdx == nil {
o.keyTypeMapping.keyFieldIdx = make(map[statusOptionKey]*statusOptionKeyToType)
}
o.keyTypeMapping.idxFieldKey[ktt.Idx] = ktt
o.keyTypeMapping.keyFieldIdx[ktt.Key] = ktt
}
func (k *statusOptionKeyToType) decodeStream(s *rlp.Stream) error {
var key statusOptionKey var key statusOptionKey
// If uint can be decoded return it // If statusOptionKey (uint) can be decoded return it
// Ignore the first error and attempt string decoding
if err := s.Decode(&key); err == nil { if err := s.Decode(&key); err == nil {
k.Key = key return key, sOKTU, nil
k.Type = sOKTU
return nil
} }
// Attempt decoding into a string // Attempt decoding into a string
var sKey string var sKey string
if err := s.Decode(&sKey); err != nil { if err := s.Decode(&sKey); err != nil {
return err return key, 0, err
} }
// Parse string into uint // Parse string into uint
uKey, err := strconv.ParseUint(sKey, 10, 64) uKey, err := strconv.ParseUint(sKey, 10, 64)
if err != nil { if err != nil {
return err return key, 0, err
} }
k.Key = statusOptionKey(uKey) key = statusOptionKey(uKey)
k.Type = sOKTS return key, sOKTS, nil
return nil
} }
func (k statusOptionKeyToType) encode() (interface{}, error) { // setKeyType sets a statusOptions' keyType if it hasn't previously been set
switch k.Type { func (o *statusOptions) setKeyType(t statusOptionKeyType) {
case sOKTU: if o.keyType == 0 {
return k.Key, nil o.keyType = t
case sOKTS:
return fmt.Sprint(k.Key), nil
default:
return nil, fmt.Errorf("failed to encode key '%d', unknown key type '%d'", k.Key, k.Type)
} }
} }
func (o statusOptions) encodeKey(key statusOptionKey) interface{} {
if o.keyType == sOKTS {
return fmt.Sprint(key)
}
return key
}
func (o statusOptions) Validate() error { func (o statusOptions) Validate() error {
if len(o.TopicInterest) > 1000 { if len(o.TopicInterest) > 1000 {
return errors.New("topic interest is limited by 1000 items") return errors.New("topic interest is limited by 1000 items")

View File

@ -25,6 +25,7 @@ func TestEncodeDecodeRLP(t *testing.T) {
TopicLimits: 1, TopicLimits: 1,
}, },
TopicInterest: []TopicType{{0x01}, {0x02}, {0x03}, {0x04}}, TopicInterest: []TopicType{{0x01}, {0x02}, {0x03}, {0x04}},
keyType: sOKTU,
} }
data, err := rlp.EncodeToBytes(opts) data, err := rlp.EncodeToBytes(opts)
require.NoError(t, err) require.NoError(t, err)