Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scale: array of strings in struct not decoding #394

Closed
noot opened this issue Nov 6, 2019 · 4 comments · Fixed by #560
Closed

scale: array of strings in struct not decoding #394

noot opened this issue Nov 6, 2019 · 4 comments · Fixed by #560
Assignees

Comments

@noot
Copy link
Contributor

noot commented Nov 6, 2019

Currently, trying to use scale to decode structs where fields slices/arrays of strings doesn't work.

eg. if we run this from the codec package:

func TestEncodeAndDecodeStringArrayInStruct(t *testing.T) {
	test := &struct{
		A []string
	} {
		A: []string{"noot", "noot2"},
	}

	enc, err := Encode(test)
	if err != nil {
		t.Fatal(err)
	}

	dec, err := Decode(enc, &struct{A []string}{A: {}})
	if err != nil {
		t.Fatal(err)
	}

	if !reflect.DeepEqual(test, dec) {
		t.Fatalf("Fail: got %v expected %v", dec, test)
	}
}

This will not work, the resulting A field will be empty: Fail: got &{} expected &{noot noot2}

A workaround right now is to cast all string arrays in a struct to [][]byte then use scale, which works fine.

for example, this works:

func TestEncodeAndDecodeByteArrayInStruct(t *testing.T) {
	test := &struct{
		A [][]byte
	} {
		A: [][]byte{[]byte("noot"), []byte("noot2")},
	}

	enc, err := Encode(test)
	if err != nil {
		t.Fatal(err)
	}

	dec, err := Decode(enc, &struct{A [][]byte}{A: [][]byte{{}}})
	if err != nil {
		t.Fatal(err)
	}

	if !reflect.DeepEqual(test, dec) {
		t.Fatalf("Fail: got %v expected %v", dec, test)
	}
}
@thomasmodeneis
Copy link
Contributor

thomasmodeneis commented Nov 26, 2019

Hi,

I am trying to figure what is wrong with the code, but I am having few issues with the examples:

The first test wont compile as Decode(enc, &struct{A []string}{A: {}}) is not valid, I think you mean: Decode(enc, &struct{A []string}{A: []string{}}) ?

The second test will fail with slice index out of range:

--- FAIL: TestEncodeAndDecodeByteArrayInStruct2 (0.00s)
panic: reflect: slice index out of range [recovered]
	panic: reflect: slice index out of range

goroutine 10 [running]:
testing.tRunner.func1(0xc08011c200)
	/usr/local/go/src/testing/testing.go:874 +0x3a3
panic(0x5423e0, 0x5a6240)
	/usr/local/go/src/runtime/panic.go:679 +0x1b2
reflect.Value.Index(0x53e840, 0xc00000c620, 0x97, 0x1, 0x538b40, 0xc00000c5c0, 0x197)
	/usr/local/go/src/reflect/value.go:949 +0x1fe
github.com/ChainSafe/gossamer/codec.(*Decoder).DecodeArray(0xc000057e80, 0x53e840, 0xc00000c620, 0xc000090be0, 0xc000090be0, 0x41261f, 0x53e840)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/decode.go:333 +0x1ea
github.com/ChainSafe/gossamer/codec.(*Decoder).DecodeInterface(0xc000090e80, 0x53e840, 0xc00000c620, 0xc00000c5a0, 0x197, 0x53e840, 0xc00000c620)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/decode.go:299 +0xc5
github.com/ChainSafe/gossamer/codec.(*Decoder).Decode(0xc000090e80, 0x53e840, 0xc00000c620, 0x1, 0x53e840, 0xc00000c620, 0x197)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/decode.go:70 +0x1ac
github.com/ChainSafe/gossamer/codec.(*Decoder).DecodeTuple(0xc000057e80, 0x53cbc0, 0xc00000c5a0, 0x550dc0, 0xc00000c5a0, 0x199, 0x44142c)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/decode.go:510 +0x5a9
github.com/ChainSafe/gossamer/codec.(*Decoder).DecodeInterface(0xc000090e80, 0x53cbc0, 0xc00000c5a0, 0x8, 0xc000090e38, 0x4b5d38, 0xc00001a580)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/decode.go:296 +0x22e
github.com/ChainSafe/gossamer/codec.(*Decoder).Decode(0xc000090e80, 0x53cbc0, 0xc00000c5a0, 0x40, 0xc, 0x0, 0x0)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/decode.go:70 +0x1ac
github.com/ChainSafe/gossamer/codec.Decode(0xc00001a540, 0xc, 0x40, 0x53cbc0, 0xc00000c5a0, 0x0, 0x0, 0x349, 0x4c0570)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/decode.go:44 +0xcc
github.com/ChainSafe/gossamer/codec.TestEncodeAndDecodeByteArrayInStruct2(0xc08011c200)
	/opt/gocode/src/github.com/ChainSafe/gossamer/codec/encode_decode_test.go:44 +0x244

Please let me know the correct test you are expecting to work, so I can try to debug :)
Cheers.

@noot
Copy link
Contributor Author

noot commented Nov 26, 2019

Hi, thanks for pointing this out! These should work as expected:

func TestEncodeAndDecodeStringArrayInStruct(t *testing.T) {
	test := &struct{
		A []string
	} {
		A: []string{"noot", "noot2"},
	}

	enc, err := Encode(test)
	if err != nil {
		t.Fatal(err) 
	}

	dec, err := Decode(enc, &struct{A []string}{A: []string{}})
	if err != nil {
		t.Fatal(err)
	}

	if !reflect.DeepEqual(test, dec) {
		t.Fatalf("Fail: got %v expected %v", dec, test)
	}
}

func TestEncodeAndDecodeByteArrayInStruct(t *testing.T) {
	test := &struct{
		A [][]byte
	} {
		A: [][]byte{[]byte("noot"), []byte("noot2")},
	}

	enc, err := Encode(test)
	if err != nil {
		t.Fatal(err)
	}

	dec, err := Decode(enc, &struct{A [][]byte}{A: [][]byte{{}, {}}})
	if err != nil {
		t.Fatal(err)
	}

	if !reflect.DeepEqual(test, dec) {
		t.Fatalf("Fail: got %v expected %v", dec, test)
	}
}

The first test (string) fails, and the second (byte array) passes.

@thomasmodeneis
Copy link
Contributor

thomasmodeneis commented Nov 29, 2019

Right, the thing is that the decode will not work because the encode is failing in the first place:

func TestEncodeAndDecodeStringArrayInStruct(t *testing.T) {
	test := &struct{
		A []string
	} {
		A: []string{"noot", "noot2"},
	}

	enc, err := Encode(test)
	if err != nil {
		t.Fatal(err)
	}

	if len(enc) == 0 {
		t.Fatal("fail to encode StringArrayInStruct")
	}

	dec, err := Decode(enc, &struct{A []*string}{A: []*string{}})
	if err != nil {
		t.Fatal(err)
	}

	if !reflect.DeepEqual(test, dec) {
		t.Fatalf("Fail: got %v expected %v", dec, test)
	}
}

Result:

-->     encode_decode_test.go:21: fail to encode StringArrayInStruct

Just a quick question, why are we implementing a custom encode/decode, could we use something like ETH RLP to get this done ?

@edwardmack
Copy link
Member

Question (to confirm if I'm understanding SCALE spec), encoding this struct:

test := &struct{
		A []string
	} {
		A: []string{"noot", "noot2"},
	}

When encode should be: [8 16 110 111 111 116 20 110 111 111 116 50]? Where:

  • Position 0: 8 - Length Encoding for 2, i.e. Array of length 2
  • Position 1: 16 - Length Encoding for 4, i.e. Array of length 4
  • Position 2 - 5: 110, 111, 111, 116 - noot
  • Position 6: 20 - Length Encoding for 5, i.e. Array of length 5
  • Position 7 - 11: 110, 111, 111, 116, 50 - noot2

Please let me know if I am understanding SCALE correctly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants