Skip to content

Commit

Permalink
Merge pull request #46 from kaleido-io/fix_decode_tuple
Browse files Browse the repository at this point in the history
fix: check dynamic type for tuple before decoding
  • Loading branch information
peterbroadhurst authored May 30, 2023
2 parents 6cf45ab + 9af5ad6 commit 038e29f
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 7 deletions.
21 changes: 16 additions & 5 deletions pkg/abi/abidecode.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,25 @@ func decodeABIElement(ctx context.Context, breadcrumbs string, block []byte, hea
}
return 32, cv, err
case TupleComponent:
headOffset, err := decodeABILength(ctx, breadcrumbs, block, headPosition)
dynamic, err := isDynamicType(ctx, component)
if err != nil {
return -1, nil, err
}
headStart += headOffset
headPosition = headStart
_, cv, err := walkDynamicChildArrayABIBytes(ctx, "tup", breadcrumbs, block, headStart, headPosition, component, component.tupleChildren)
return 32, cv, err
if dynamic {
headOffset, err := decodeABILength(ctx, breadcrumbs, block, headPosition)
if err != nil {
return -1, nil, err
}
headStart += headOffset
headPosition = headStart
}

headBytesRead, cv, err := walkDynamicChildArrayABIBytes(ctx, "tup", breadcrumbs, block, headStart, headPosition, component, component.tupleChildren)
if dynamic {
// In the case where it's dynamic we only read one block
headBytesRead = 32
}
return headBytesRead, cv, err
default:
return -1, nil, i18n.NewError(ctx, signermsgs.MsgBadABITypeComponent, component.cType)
}
Expand Down
150 changes: 149 additions & 1 deletion pkg/abi/abidecode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,35 @@ func TestExampleABIDecodeTuple(t *testing.T) {
assert.Equal(t, big.NewInt(2414), cv.Children[0].Children[0].Children[3].Value)
}

// TestExampleABIDecodeNonDynamicTuple is responsible for testing a duple that does
// not have any dynamic fields
func TestExampleABIDecodeNonDynamicTuple(t *testing.T) {

f := &Entry{
Name: "coupon",
Outputs: ParameterArray{
{
Type: "tuple",
Name: "mystruct",
Components: ParameterArray{
{Type: "address", Name: "account"},
{Type: "uint256", Name: "value"},
},
},
},
}

d, _ := hex.DecodeString("" +
"000000000000000000000000d2d8a61d774f552301d71aa99a78aff2e4765ca7" +
"000000000000000000000000000000000000000000000000000000000000007b")

cv, err := f.Outputs.DecodeABIData(d, 0)
assert.NoError(t, err)
assert.Equal(t, big.NewInt(123), cv.Children[0].Children[1].Value)
address, _ := cv.Children[0].Children[0].JSON()
assert.Equal(t, "\"d2d8a61d774f552301d71aa99a78aff2e4765ca7\"", string(address))
}

func TestExampleABIDecodeDoubleNestedTuple(t *testing.T) {

f := &Entry{
Expand Down Expand Up @@ -410,6 +439,112 @@ func TestExampleABIDecodeDoubleNestedTuple(t *testing.T) {
assert.Equal(t, big.NewInt(66666), cv.Children[0].Children[0].Children[3].Value)
}

func TestExampleABIDecodeStaticNestedTupleInDynamicTuple(t *testing.T) {

f := &Entry{
Name: "f",
Outputs: ParameterArray{
{
Type: "tuple[]",
Name: "nested",
Components: ParameterArray{
{Type: "uint256", Name: "a"},
{Type: "string", Name: "b"},
{Type: "tuple", Name: "c", Components: ParameterArray{
{Type: "uint256", Name: "c1"},
{Type: "uint256", Name: "c2"},
}},
{Type: "uint256", Name: "d"},
},
},
},
}

b, err := f.Outputs.EncodeABIDataJSON([]byte(`{
"nested": [{
"a": 11111,
"b": "test22222",
"c": {
"c1": 33333,
"c2": 44444
},
"d": 55555
}]
}`))
assert.NoError(t, err)

assert.Equal(t,
"0000000000000000000000000000000000000000000000000000000000000020"+ // 0 - 32 - offset for the start of the dynamic array
"0000000000000000000000000000000000000000000000000000000000000001"+ // 32 - 1 - number of tuples in the dynamic array
"0000000000000000000000000000000000000000000000000000000000000020"+ // 64 - 32 - offset of the data for the tuple at position 0 in the array
"0000000000000000000000000000000000000000000000000000000000002b67"+ // 96 - 11111 - value "a"
"00000000000000000000000000000000000000000000000000000000000000a0"+ // 128 - 160 - offset of the string data for "b", relative to 96 = 224
"0000000000000000000000000000000000000000000000000000000000008235"+ // 160 - 33333 - value of "c1"
"000000000000000000000000000000000000000000000000000000000000ad9c"+ // 192 - 44444 - value of "c2"
"000000000000000000000000000000000000000000000000000000000000d903"+ // 224 - 55555 - value of "d"
"0000000000000000000000000000000000000000000000000000000000000009"+ // 256 - 9 - length of the string data for "b"
"7465737432323232320000000000000000000000000000000000000000000000", // 288 - "test22222"
hex.EncodeToString(b))

cv, err := f.Outputs.DecodeABIData(b, 0)

assert.Equal(t, big.NewInt(11111), cv.Children[0].Children[0].Children[0].Value)
assert.Equal(t, "test22222", cv.Children[0].Children[0].Children[1].Value)
assert.Equal(t, big.NewInt(33333), cv.Children[0].Children[0].Children[2].Children[0].Value)
assert.Equal(t, big.NewInt(44444), cv.Children[0].Children[0].Children[2].Children[1].Value)
assert.Equal(t, big.NewInt(55555), cv.Children[0].Children[0].Children[3].Value)
}

func TestExampleABIDecodeDoubleStaticNestedTuple(t *testing.T) {

f := &Entry{
Name: "f",
Outputs: ParameterArray{
{
Type: "tuple[]",
Name: "nested",
Components: ParameterArray{
{Type: "uint256", Name: "a"},
{Type: "tuple", Name: "c", Components: ParameterArray{
{Type: "uint256", Name: "c1"},
{Type: "uint256", Name: "c2"},
}},
{Type: "uint256", Name: "d"},
},
},
},
}

b, err := f.Outputs.EncodeABIDataJSON([]byte(`{
"nested": [{
"a": 11111,
"c": {
"c1": 22222,
"c2": 33333
},
"d": 44444
}]
}`))
assert.NoError(t, err)

// The encoding looks right to me
assert.Equal(t,
"0000000000000000000000000000000000000000000000000000000000000020"+ // 0 - 32 - offset for the start of the dynamic array
"0000000000000000000000000000000000000000000000000000000000000001"+ // 32 - 1 - number of tuples in the dynamic array
"0000000000000000000000000000000000000000000000000000000000002b67"+ // 64 - 11111 - value of "a"
"00000000000000000000000000000000000000000000000000000000000056ce"+ // 96 - 22222 - value of "c1"
"0000000000000000000000000000000000000000000000000000000000008235"+ // 128 - 33333 - value of "c2"
"000000000000000000000000000000000000000000000000000000000000ad9c", // 160 - 44444 - value of "d"
hex.EncodeToString(b))

cv, err := f.Outputs.DecodeABIData(b, 0)

assert.Equal(t, big.NewInt(11111), cv.Children[0].Children[0].Children[0].Value)
assert.Equal(t, big.NewInt(22222), cv.Children[0].Children[0].Children[1].Children[0].Value)
assert.Equal(t, big.NewInt(33333), cv.Children[0].Children[0].Children[1].Children[1].Value)
assert.Equal(t, big.NewInt(44444), cv.Children[0].Children[0].Children[2].Value)
}

func TestExampleABIDecodeTupleFixed(t *testing.T) {

f := &Entry{
Expand Down Expand Up @@ -834,7 +969,6 @@ func TestIsDynamicTypeBadNestedTupleType(t *testing.T) {
})
assert.Regexp(t, "FF22041", err)
}

func TestDecodeABIElementBadDynamicTypeFixedArray(t *testing.T) {

block, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000020")
Expand All @@ -848,6 +982,20 @@ func TestDecodeABIElementBadDynamicTypeFixedArray(t *testing.T) {
assert.Regexp(t, "FF22041", err)
}

func TestDecodeABIElementBadDynamicTypeTuple(t *testing.T) {

block, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000020")
assert.NoError(t, err)

_, _, err = decodeABIElement(context.Background(), "", block, 0, 0, &typeComponent{
cType: TupleComponent,
tupleChildren: []*typeComponent{
{cType: 99},
},
})
assert.Regexp(t, "FF22041", err)
}

func TestDecodeABIElementInsufficientDataFixedArrayDynamicType(t *testing.T) {

p := &ParameterArray{
Expand Down
2 changes: 1 addition & 1 deletion pkg/abi/abiencode.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (cv *ComponentValue) encodeABIData(ctx context.Context, desc string) ([]byt
case DynamicArrayComponent:
return cv.encodeABIChildren(ctx, desc, true /* always dynamic */, true /* need length */)
case TupleComponent:
return cv.encodeABIChildren(ctx, desc, true /* always dynamic */, false /* no length */)
return cv.encodeABIChildren(ctx, desc, false /* only dynamic if the children are dynamic */, false /* no length */)
default:
return nil, false, i18n.NewError(ctx, signermsgs.MsgBadABITypeComponent, tc.cType)
}
Expand Down

0 comments on commit 038e29f

Please sign in to comment.