Added better prediction of waku RLP key type and value

This commit is contained in:
Samuel Hawksby-Robinson 2020-04-14 22:52:34 +01:00 committed by Andrea Maria Piana
parent a50e35b72d
commit 900bad769a
2 changed files with 106 additions and 24 deletions

View File

@ -1,8 +1,11 @@
package waku
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/davecgh/go-spew/spew"
"io"
"math"
"reflect"
@ -202,28 +205,36 @@ loop:
}
func (o statusOptions) decodeKey(s *rlp.Stream) (statusOptionKey, statusOptionKeyType, error) {
var key statusOptionKey
// Problem: A string will be encoded to bytes, and bytes can be decoded into a uint.
// This means that an encoded string that is attempted to be decoded into a uint will succeed and return a valid uint.
// This is bad because wildly inaccurate keys can be returned. See below examples:
// - string("0"); encodes to byte(48); decodes to uint(48).
// - string("111"); encodes to []byte(131, 49, 49, 49); decode to uint(3223857).
// This means an expected index of 0 will be returned as 48. An expected index of 111 will be returned as 3223857
// If statusOptionKey (uint) can be decoded return it
// Ignore the first error and attempt string decoding
if err := s.Decode(&key); err == nil {
return key, sOKTU, nil
}
// Solution: We need to first test if the RLP stream can be decoded into a string.
// If a stream can be decoded into a string, attempt to decode the string into a uint.
// If decoding the string into a uint is successful return the value.
// If decoding the string failed, attempt to decode as a uint. Return the result or error from this final step.
// Attempt decoding into a string
var sKey string
if err := s.Decode(&sKey); err != nil {
return key, 0, err
// decode into bytes, detect if bytes can be parsed as a string and from a string to a uint
var bKey []byte
if err := s.Decode(&bKey); err != nil {
return 0, 0, err
}
// Parse string into uint
uKey, err := strconv.ParseUint(sKey, 10, 64)
if err != nil {
return key, 0, err
uKey, err := strconv.ParseUint(string(bKey), 10, 64)
if err == nil {
return statusOptionKey(uKey), sOKTS, err
}
key = statusOptionKey(uKey)
return key, sOKTS, nil
// If statusOptionKey (uint) can be decoded return it
buf := bytes.NewBuffer(bKey)
uintKey, c := binary.ReadUvarint(buf)
spew.Dump(uintKey, c)
return statusOptionKey(uintKey), sOKTU, nil
}
// setKeyType sets a statusOptions' keyType if it hasn't previously been set

View File

@ -1,6 +1,8 @@
package waku
import (
"fmt"
"github.com/davecgh/go-spew/spew"
"math"
"testing"
@ -36,18 +38,87 @@ func TestEncodeDecodeRLP(t *testing.T) {
require.EqualValues(t, opts, optsDecoded)
}
func TestBackwardCompatibility(t *testing.T) {
alist := []interface{}{
[]interface{}{"0", math.Float64bits(2.05)},
// TODO remove once key type issue is resolved.
func TestKeyTypes(t *testing.T) {
uKeys := []uint{
0, 1, 2, 49, 50, 256, 257, 1000, 6000,
}
data, err := rlp.EncodeToBytes(alist)
require.NoError(t, err)
var optsDecoded statusOptions
err = rlp.DecodeBytes(data, &optsDecoded)
require.NoError(t, err)
for i, uKey := range uKeys {
fmt.Printf("test %d, for key '%d'", i+1, uKey)
encodeable := []interface{}{
[]interface{}{uKey, true},
}
data, err := rlp.EncodeToBytes(encodeable)
spew.Dump(data, err)
var optsDecoded statusOptions
err = rlp.DecodeBytes(data, &optsDecoded)
spew.Dump(optsDecoded, err)
println("\n----------------\n")
}
}
func TestBackwardCompatibility(t *testing.T) {
pow := math.Float64bits(2.05)
require.EqualValues(t, statusOptions{PoWRequirement: &pow}, optsDecoded)
lne := true
cs := []struct {
Input []interface{}
Expected statusOptions
}{
{
[]interface{}{
[]interface{}{"0", pow},
},
statusOptions{PoWRequirement: &pow, keyType: sOKTS},
},
{
[]interface{}{
[]interface{}{"2", true},
},
statusOptions{LightNodeEnabled: &lne, keyType: sOKTS},
},
{
[]interface{}{
[]interface{}{uint(2), true},
},
statusOptions{LightNodeEnabled: &lne, keyType: sOKTU},
},
{
[]interface{}{
[]interface{}{uint(0), pow},
},
statusOptions{PoWRequirement: &pow, keyType: sOKTU},
},
{
[]interface{}{
[]interface{}{"1000", true},
},
statusOptions{keyType: sOKTS},
},
{
[]interface{}{
[]interface{}{uint(1000), true},
},
statusOptions{keyType: sOKTU},
},
}
for i, c := range cs {
failMsg := fmt.Sprintf("test '%d'", i+1)
data, err := rlp.EncodeToBytes(c.Input)
require.NoError(t, err, failMsg)
var optsDecoded statusOptions
err = rlp.DecodeBytes(data, &optsDecoded)
require.NoError(t, err, failMsg)
require.EqualValues(t, c.Expected, optsDecoded, failMsg)
}
}
func TestForwardCompatibility(t *testing.T) {