-
Notifications
You must be signed in to change notification settings - Fork 20.4k
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
accounts/abi: add another unpack interface #15770
Conversation
I've mow modified this PR, so that the more complex unpack-into-struct uses the |
(which also means that I could remove the unpack_testV2 file) |
1bb8865
to
1b235e1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a minor comment for using an iterator pattern instead of building an array on the fly, otherwise looks good to me.
accounts/abi/argument.go
Outdated
@@ -67,59 67,55 @@ func (arguments Arguments) LengthNonIndexed() int { | |||
return out | |||
} | |||
|
|||
func (arguments Arguments) NonIndexed() Arguments { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't it make sense to rewrite it as an iterator, since it's mostly called with range
and the only time that you don't, you only need the first value? That would avoid parsing the entire array if you don't need to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I like this PR, since it separates the ABI decoding a bit and adds an intermediate layer in between that we can unit test.
A question however is whether we can attack this intermediate layer? When marshaling into explicit Go types, we immediately discard binary data that doesn't match what we expect. Here however we accept any meaningful data and only validate it later.
I.e. I think it would be very very wise to run this code through gofuzz for starters, and also to explore whether it could go OOM with an appropriately constructed input.
@@ -137,11 137,11 @@ func (abi *ABI) UnmarshalJSON(data []byte) error { | |||
|
|||
// MethodById looks up a method by the 4-byte id | |||
// returns nil if none found | |||
func (abi *ABI) MethodById(sigdata []byte) *Method { | |||
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could use [4]byte
here to compile time ensure the size is ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could, but then the caller needs to convert his slice of bytes into an array instead, with some for-loop. Is that really nicer?
accounts/abi/abi.go
Outdated
} | ||
} | ||
return nil | ||
return nil, fmt.Errorf("ABI spec does not contain method signature in data: 0x%x", sigdata[:4]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally Go uses shorter error messages since they tend to get appended to one another. If possible, try to make it a bit more concise. E.g. fmt.Errorf("no method with id %#x", sigdata[:4])
Note 0x%x
is the same at %#x
for formatting.
accounts/abi/abi_test.go
Outdated
b := fmt.Sprintf("%v", abi.MethodById(m.Id())) | ||
m2, err := abi.MethodById(m.Id()) | ||
if err != nil { | ||
t.Fatal(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this is where you append errors :)
t.Fatalf("Failed to look up ABI method: %v", err)
accounts/abi/argument.go
Outdated
@@ -67,59 67,55 @@ func (arguments Arguments) LengthNonIndexed() int { | |||
return out | |||
} | |||
|
|||
func (arguments Arguments) NonIndexed() Arguments { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accounts/abi/argument.go
Outdated
if err != nil { | ||
return err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick, but why so many empty lines? They are useful for explicitly separating logical blocks, but here the }
line in itself is already a nice visual separator so adding 1 empty line seems a tad wasteful.
accounts/abi/argument.go
Outdated
retval := make([]interface{}, 0, arguments.LengthNonIndexed()) | ||
|
||
virtualArgs := 0 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why an empty line?
accounts/abi/argument.go
Outdated
marshalledValue, err := toGoType((index virtualArgs)*32, arg.Type, data) | ||
|
||
if arg.Type.T == ArrayTy { | ||
//If we have a static array, like [3]uint256, these are coded as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space between //
and ``If`
accounts/abi/argument.go
Outdated
return retval, nil | ||
} | ||
|
||
// UnpackValues performs the operation Go format -> Hexdata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UnpackValues
-> PackValues
accounts/abi/argument.go
Outdated
|
||
// UnpackValues performs the operation Go format -> Hexdata | ||
// It is the semantic opposite of UnpackValues | ||
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this method, if we can just call Pack(args...)
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need it, I just added to have coder<->decoder pipe with identical input<->output format. LMK if I should remove it.
accounts/abi/unpackv2_test.go
Outdated
@@ -0,0 1,335 @@ | |||
// Copyright 2015 The go-ethereum Authors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's with the v2
stuff?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to remove this file. Since this is a copy of the existing one, but uses the 'raw' unpacker, this is now not needed since they complex parsing happens on top of the 'raw' parsing.
Good point. I'll experiment a bit with that. |
It does handle very large arrays, and detects out-of-bound size: |
The original code kind of intentionally ignored all
|
It's not quite the case, thought. The |
Rebased on master |
Note: I don't really care if we do that, or if we keep it as it was previously. It almost looks like the previous code was written like that for a purpose (?) |
Ping @karalabe |
0a7142f
to
61f2279
Compare
This PR adds the ability to unpack ABI-encoded data without supplying a data structure to place the data into.
It allows the caller to do
method.Inputs.UnpackValues(data)
, and receive a[]interface{}
containing the data in whatever type is used by the abi. This enables the usecase that the actual abi-spec is not known in advance, such as when the signer wants to decode arbitrary data.The old API-methods are left intact; when a programmer wants to interact with a specific (known) ABI, it's probably (?) useful to be able to handles custom structs instead of casting suff on-the-fly.
This PR uses the same set of testcases that was already in use for
Unpack
, and use them to test the new method(s). The PR does (should) not interfere with existing code/features, and should be fairly safe to merge.