Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
7ca40306
Commit
7ca40306
authored
Jan 10, 2019
by
gary rong
Committed by
Guillaume Ballet
Jan 10, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
accounts/abi: tuple support (#18406)
parent
6df3e4ee
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1003 additions
and
393 deletions
+1003
-393
abi.go
accounts/abi/abi.go
+0
-2
abi_test.go
accounts/abi/abi_test.go
+40
-17
argument.go
accounts/abi/argument.go
+117
-70
pack_test.go
accounts/abi/pack_test.go
+291
-11
reflect.go
accounts/abi/reflect.go
+27
-36
type.go
accounts/abi/type.go
+122
-19
type_test.go
accounts/abi/type_test.go
+226
-207
unpack.go
accounts/abi/unpack.go
+64
-28
unpack_test.go
accounts/abi/unpack_test.go
+116
-3
No files found.
accounts/abi/abi.go
View file @
7ca40306
...
...
@@ -58,13 +58,11 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return
nil
,
err
}
return
arguments
,
nil
}
method
,
exist
:=
abi
.
Methods
[
name
]
if
!
exist
{
return
nil
,
fmt
.
Errorf
(
"method '%s' not found"
,
name
)
}
arguments
,
err
:=
method
.
Inputs
.
Pack
(
args
...
)
if
err
!=
nil
{
return
nil
,
err
...
...
accounts/abi/abi_test.go
View file @
7ca40306
...
...
@@ -22,11 +22,10 @@ import (
"fmt"
"log"
"math/big"
"reflect"
"strings"
"testing"
"reflect"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
...
...
@@ -52,11 +51,14 @@ const jsondata2 = `
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] }
{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
{ "type" : "function", "name" : "nestedArray", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
{ "type" : "function", "name" : "nestedArray2", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
{ "type" : "function", "name" : "nestedSlice", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] }
]`
func
TestReader
(
t
*
testing
.
T
)
{
Uint256
,
_
:=
NewType
(
"uint256"
)
Uint256
,
_
:=
NewType
(
"uint256"
,
nil
)
exp
:=
ABI
{
Methods
:
map
[
string
]
Method
{
"balance"
:
{
...
...
@@ -177,7 +179,7 @@ func TestTestSlice(t *testing.T) {
}
func
TestMethodSignature
(
t
*
testing
.
T
)
{
String
,
_
:=
NewType
(
"string"
)
String
,
_
:=
NewType
(
"string"
,
nil
)
m
:=
Method
{
"foo"
,
false
,
[]
Argument
{{
"bar"
,
String
,
false
},
{
"baz"
,
String
,
false
}},
nil
}
exp
:=
"foo(string,string)"
if
m
.
Sig
()
!=
exp
{
...
...
@@ -189,12 +191,31 @@ func TestMethodSignature(t *testing.T) {
t
.
Errorf
(
"expected ids to match %x != %x"
,
m
.
Id
(),
idexp
)
}
uintt
,
_
:=
NewType
(
"uint256"
)
uintt
,
_
:=
NewType
(
"uint256"
,
nil
)
m
=
Method
{
"foo"
,
false
,
[]
Argument
{{
"bar"
,
uintt
,
false
}},
nil
}
exp
=
"foo(uint256)"
if
m
.
Sig
()
!=
exp
{
t
.
Error
(
"signature mismatch"
,
exp
,
"!="
,
m
.
Sig
())
}
// Method with tuple arguments
s
,
_
:=
NewType
(
"tuple"
,
[]
ArgumentMarshaling
{
{
Name
:
"a"
,
Type
:
"int256"
},
{
Name
:
"b"
,
Type
:
"int256[]"
},
{
Name
:
"c"
,
Type
:
"tuple[]"
,
Components
:
[]
ArgumentMarshaling
{
{
Name
:
"x"
,
Type
:
"int256"
},
{
Name
:
"y"
,
Type
:
"int256"
},
}},
{
Name
:
"d"
,
Type
:
"tuple[2]"
,
Components
:
[]
ArgumentMarshaling
{
{
Name
:
"x"
,
Type
:
"int256"
},
{
Name
:
"y"
,
Type
:
"int256"
},
}},
})
m
=
Method
{
"foo"
,
false
,
[]
Argument
{{
"s"
,
s
,
false
},
{
"bar"
,
String
,
false
}},
nil
}
exp
=
"foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
if
m
.
Sig
()
!=
exp
{
t
.
Error
(
"signature mismatch"
,
exp
,
"!="
,
m
.
Sig
())
}
}
func
TestMultiPack
(
t
*
testing
.
T
)
{
...
...
@@ -564,11 +585,13 @@ func TestBareEvents(t *testing.T) {
const
definition
=
`[
{ "type" : "event", "name" : "balance" },
{ "type" : "event", "name" : "anon", "anonymous" : true},
{ "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] }
{ "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] },
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
]`
arg0
,
_
:=
NewType
(
"uint256"
)
arg1
,
_
:=
NewType
(
"address"
)
arg0
,
_
:=
NewType
(
"uint256"
,
nil
)
arg1
,
_
:=
NewType
(
"address"
,
nil
)
tuple
,
_
:=
NewType
(
"tuple"
,
[]
ArgumentMarshaling
{{
Name
:
"a"
,
Type
:
"uint256"
}})
expectedEvents
:=
map
[
string
]
struct
{
Anonymous
bool
...
...
@@ -580,6 +603,10 @@ func TestBareEvents(t *testing.T) {
{
Name
:
"arg0"
,
Type
:
arg0
,
Indexed
:
false
},
{
Name
:
"arg1"
,
Type
:
arg1
,
Indexed
:
true
},
}},
"tuple"
:
{
false
,
[]
Argument
{
{
Name
:
"t"
,
Type
:
tuple
,
Indexed
:
false
},
{
Name
:
"arg1"
,
Type
:
arg1
,
Indexed
:
true
},
}},
}
abi
,
err
:=
JSON
(
strings
.
NewReader
(
definition
))
...
...
@@ -646,28 +673,24 @@ func TestUnpackEvent(t *testing.T) {
}
type
ReceivedEvent
struct
{
Address
common
.
Address
Amount
*
big
.
Int
Memo
[]
byte
Sender
common
.
Address
Amount
*
big
.
Int
Memo
[]
byte
}
var
ev
ReceivedEvent
err
=
abi
.
Unpack
(
&
ev
,
"received"
,
data
)
if
err
!=
nil
{
t
.
Error
(
err
)
}
else
{
t
.
Logf
(
"len(data): %d; received event: %+v"
,
len
(
data
),
ev
)
}
type
ReceivedAddrEvent
struct
{
Address
common
.
Address
Sender
common
.
Address
}
var
receivedAddrEv
ReceivedAddrEvent
err
=
abi
.
Unpack
(
&
receivedAddrEv
,
"receivedAddr"
,
data
)
if
err
!=
nil
{
t
.
Error
(
err
)
}
else
{
t
.
Logf
(
"len(data): %d; received event: %+v"
,
len
(
data
),
receivedAddrEv
)
}
}
...
...
accounts/abi/argument.go
View file @
7ca40306
...
...
@@ -33,24 +33,27 @@ type Argument struct {
type
Arguments
[]
Argument
type
ArgumentMarshaling
struct
{
Name
string
Type
string
Components
[]
ArgumentMarshaling
Indexed
bool
}
// UnmarshalJSON implements json.Unmarshaler interface
func
(
argument
*
Argument
)
UnmarshalJSON
(
data
[]
byte
)
error
{
var
extarg
struct
{
Name
string
Type
string
Indexed
bool
}
err
:=
json
.
Unmarshal
(
data
,
&
extarg
)
var
arg
ArgumentMarshaling
err
:=
json
.
Unmarshal
(
data
,
&
arg
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"argument json err: %v"
,
err
)
}
argument
.
Type
,
err
=
NewType
(
extarg
.
Type
)
argument
.
Type
,
err
=
NewType
(
arg
.
Type
,
arg
.
Components
)
if
err
!=
nil
{
return
err
}
argument
.
Name
=
ext
arg
.
Name
argument
.
Indexed
=
ext
arg
.
Indexed
argument
.
Name
=
arg
.
Name
argument
.
Indexed
=
arg
.
Indexed
return
nil
}
...
...
@@ -85,7 +88,6 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format
func
(
arguments
Arguments
)
Unpack
(
v
interface
{},
data
[]
byte
)
error
{
// make sure the passed value is arguments pointer
if
reflect
.
Ptr
!=
reflect
.
ValueOf
(
v
)
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
...
...
@@ -97,52 +99,134 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
if
arguments
.
isTuple
()
{
return
arguments
.
unpackTuple
(
v
,
marshalledValues
)
}
return
arguments
.
unpackAtomic
(
v
,
marshalledValues
)
return
arguments
.
unpackAtomic
(
v
,
marshalledValues
[
0
]
)
}
func
(
arguments
Arguments
)
unpackTuple
(
v
interface
{},
marshalledValues
[]
interface
{})
error
{
// unpack sets the unmarshalled value to go format.
// Note the dst here must be settable.
func
unpack
(
t
*
Type
,
dst
interface
{},
src
interface
{})
error
{
var
(
dstVal
=
reflect
.
ValueOf
(
dst
)
.
Elem
()
srcVal
=
reflect
.
ValueOf
(
src
)
)
if
t
.
T
!=
TupleTy
&&
!
((
t
.
T
==
SliceTy
||
t
.
T
==
ArrayTy
)
&&
t
.
Elem
.
T
==
TupleTy
)
{
return
set
(
dstVal
,
srcVal
)
}
switch
t
.
T
{
case
TupleTy
:
if
dstVal
.
Kind
()
!=
reflect
.
Struct
{
return
fmt
.
Errorf
(
"abi: invalid dst value for unpack, want struct, got %s"
,
dstVal
.
Kind
())
}
fieldmap
,
err
:=
mapArgNamesToStructFields
(
t
.
TupleRawNames
,
dstVal
)
if
err
!=
nil
{
return
err
}
for
i
,
elem
:=
range
t
.
TupleElems
{
fname
:=
fieldmap
[
t
.
TupleRawNames
[
i
]]
field
:=
dstVal
.
FieldByName
(
fname
)
if
!
field
.
IsValid
()
{
return
fmt
.
Errorf
(
"abi: field %s can't found in the given value"
,
t
.
TupleRawNames
[
i
])
}
if
err
:=
unpack
(
elem
,
field
.
Addr
()
.
Interface
(),
srcVal
.
Field
(
i
)
.
Interface
());
err
!=
nil
{
return
err
}
}
return
nil
case
SliceTy
:
if
dstVal
.
Kind
()
!=
reflect
.
Slice
{
return
fmt
.
Errorf
(
"abi: invalid dst value for unpack, want slice, got %s"
,
dstVal
.
Kind
())
}
slice
:=
reflect
.
MakeSlice
(
dstVal
.
Type
(),
srcVal
.
Len
(),
srcVal
.
Len
())
for
i
:=
0
;
i
<
slice
.
Len
();
i
++
{
if
err
:=
unpack
(
t
.
Elem
,
slice
.
Index
(
i
)
.
Addr
()
.
Interface
(),
srcVal
.
Index
(
i
)
.
Interface
());
err
!=
nil
{
return
err
}
}
dstVal
.
Set
(
slice
)
case
ArrayTy
:
if
dstVal
.
Kind
()
!=
reflect
.
Array
{
return
fmt
.
Errorf
(
"abi: invalid dst value for unpack, want array, got %s"
,
dstVal
.
Kind
())
}
array
:=
reflect
.
New
(
dstVal
.
Type
())
.
Elem
()
for
i
:=
0
;
i
<
array
.
Len
();
i
++
{
if
err
:=
unpack
(
t
.
Elem
,
array
.
Index
(
i
)
.
Addr
()
.
Interface
(),
srcVal
.
Index
(
i
)
.
Interface
());
err
!=
nil
{
return
err
}
}
dstVal
.
Set
(
array
)
}
return
nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func
(
arguments
Arguments
)
unpackAtomic
(
v
interface
{},
marshalledValues
interface
{})
error
{
if
arguments
.
LengthNonIndexed
()
==
0
{
return
nil
}
argument
:=
arguments
.
NonIndexed
()[
0
]
elem
:=
reflect
.
ValueOf
(
v
)
.
Elem
()
if
elem
.
Kind
()
==
reflect
.
Struct
{
fieldmap
,
err
:=
mapArgNamesToStructFields
([]
string
{
argument
.
Name
},
elem
)
if
err
!=
nil
{
return
err
}
field
:=
elem
.
FieldByName
(
fieldmap
[
argument
.
Name
])
if
!
field
.
IsValid
()
{
return
fmt
.
Errorf
(
"abi: field %s can't be found in the given value"
,
argument
.
Name
)
}
return
unpack
(
&
argument
.
Type
,
field
.
Addr
()
.
Interface
(),
marshalledValues
)
}
return
unpack
(
&
argument
.
Type
,
elem
.
Addr
()
.
Interface
(),
marshalledValues
)
}
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
func
(
arguments
Arguments
)
unpackTuple
(
v
interface
{},
marshalledValues
[]
interface
{})
error
{
var
(
value
=
reflect
.
ValueOf
(
v
)
.
Elem
()
typ
=
value
.
Type
()
kind
=
value
.
Kind
()
)
if
err
:=
requireUnpackKind
(
value
,
typ
,
kind
,
arguments
);
err
!=
nil
{
return
err
}
// If the interface is a struct, get of abi->struct_field mapping
var
abi2struct
map
[
string
]
string
if
kind
==
reflect
.
Struct
{
var
err
error
abi2struct
,
err
=
mapAbiToStructFields
(
arguments
,
value
)
var
(
argNames
[]
string
err
error
)
for
_
,
arg
:=
range
arguments
.
NonIndexed
()
{
argNames
=
append
(
argNames
,
arg
.
Name
)
}
abi2struct
,
err
=
mapArgNamesToStructFields
(
argNames
,
value
)
if
err
!=
nil
{
return
err
}
}
for
i
,
arg
:=
range
arguments
.
NonIndexed
()
{
reflectValue
:=
reflect
.
ValueOf
(
marshalledValues
[
i
])
switch
kind
{
case
reflect
.
Struct
:
if
structField
,
ok
:=
abi2struct
[
arg
.
Name
];
ok
{
if
err
:=
set
(
value
.
FieldByName
(
structField
),
reflectValue
,
arg
);
err
!=
nil
{
return
err
}
field
:=
value
.
FieldByName
(
abi2struct
[
arg
.
Name
])
if
!
field
.
IsValid
()
{
return
fmt
.
Errorf
(
"abi: field %s can't be found in the given value"
,
arg
.
Name
)
}
if
err
:=
unpack
(
&
arg
.
Type
,
field
.
Addr
()
.
Interface
(),
marshalledValues
[
i
]);
err
!=
nil
{
return
err
}
case
reflect
.
Slice
,
reflect
.
Array
:
if
value
.
Len
()
<
i
{
return
fmt
.
Errorf
(
"abi: insufficient number of arguments for unpack, want %d, got %d"
,
len
(
arguments
),
value
.
Len
())
}
v
:=
value
.
Index
(
i
)
if
err
:=
requireAssignable
(
v
,
reflect
Value
);
err
!=
nil
{
if
err
:=
requireAssignable
(
v
,
reflect
.
ValueOf
(
marshalledValues
[
i
])
);
err
!=
nil
{
return
err
}
if
err
:=
set
(
v
.
Elem
(),
reflectValue
,
arg
);
err
!=
nil
{
if
err
:=
unpack
(
&
arg
.
Type
,
v
.
Addr
()
.
Interface
(),
marshalledValues
[
i
]);
err
!=
nil
{
return
err
}
default
:
...
...
@@ -150,48 +234,7 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
}
}
return
nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func
(
arguments
Arguments
)
unpackAtomic
(
v
interface
{},
marshalledValues
[]
interface
{})
error
{
if
len
(
marshalledValues
)
!=
1
{
return
fmt
.
Errorf
(
"abi: wrong length, expected single value, got %d"
,
len
(
marshalledValues
))
}
elem
:=
reflect
.
ValueOf
(
v
)
.
Elem
()
kind
:=
elem
.
Kind
()
reflectValue
:=
reflect
.
ValueOf
(
marshalledValues
[
0
])
var
abi2struct
map
[
string
]
string
if
kind
==
reflect
.
Struct
{
var
err
error
if
abi2struct
,
err
=
mapAbiToStructFields
(
arguments
,
elem
);
err
!=
nil
{
return
err
}
arg
:=
arguments
.
NonIndexed
()[
0
]
if
structField
,
ok
:=
abi2struct
[
arg
.
Name
];
ok
{
return
set
(
elem
.
FieldByName
(
structField
),
reflectValue
,
arg
)
}
return
nil
}
return
set
(
elem
,
reflectValue
,
arguments
.
NonIndexed
()[
0
])
}
// Computes the full size of an array;
// i.e. counting nested arrays, which count towards size for unpacking.
func
getArraySize
(
arr
*
Type
)
int
{
size
:=
arr
.
Size
// Arrays can be nested, with each element being the same size
arr
=
arr
.
Elem
for
arr
.
T
==
ArrayTy
{
// Keep multiplying by elem.Size while the elem is an array.
size
*=
arr
.
Size
arr
=
arr
.
Elem
}
// Now we have the full array size, including its children.
return
size
}
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
...
...
@@ -202,7 +245,7 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
virtualArgs
:=
0
for
index
,
arg
:=
range
arguments
.
NonIndexed
()
{
marshalledValue
,
err
:=
toGoType
((
index
+
virtualArgs
)
*
32
,
arg
.
Type
,
data
)
if
arg
.
Type
.
T
==
ArrayTy
&&
(
*
arg
.
Type
.
Elem
)
.
T
!=
StringTy
{
if
arg
.
Type
.
T
==
ArrayTy
&&
!
isDynamicType
(
arg
.
Type
)
{
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
...
...
@@ -213,7 +256,11 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
//
// Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied.
virtualArgs
+=
getArraySize
(
&
arg
.
Type
)
-
1
virtualArgs
+=
getTypeSize
(
arg
.
Type
)
/
32
-
1
}
else
if
arg
.
Type
.
T
==
TupleTy
&&
!
isDynamicType
(
arg
.
Type
)
{
// If we have a static tuple, like (uint256, bool, uint256), these are
// coded as just like uint256,bool,uint256
virtualArgs
+=
getTypeSize
(
arg
.
Type
)
/
32
-
1
}
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -243,7 +290,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// input offset is the bytes offset for packed output
inputOffset
:=
0
for
_
,
abiArg
:=
range
abiArgs
{
inputOffset
+=
get
DynamicTypeOffset
(
abiArg
.
Type
)
inputOffset
+=
get
TypeSize
(
abiArg
.
Type
)
}
var
ret
[]
byte
for
i
,
a
:=
range
args
{
...
...
accounts/abi/pack_test.go
View file @
7ca40306
This diff is collapsed.
Click to expand it.
accounts/abi/reflect.go
View file @
7ca40306
...
...
@@ -71,18 +71,17 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
//
// set is a bit more lenient when it comes to assignment and doesn't force an as
// strict ruleset as bare `reflect` does.
func
set
(
dst
,
src
reflect
.
Value
,
output
Argument
)
error
{
dstType
:=
dst
.
Type
()
srcType
:=
src
.
Type
()
func
set
(
dst
,
src
reflect
.
Value
)
error
{
dstType
,
srcType
:=
dst
.
Type
(),
src
.
Type
()
switch
{
case
dstType
.
AssignableTo
(
srcType
)
:
dst
.
Set
(
src
)
case
dstType
.
Kind
()
==
reflect
.
Slice
&&
srcType
.
Kind
()
==
reflect
.
Slice
:
return
setSlice
(
dst
,
src
,
output
)
case
dstType
.
Kind
()
==
reflect
.
Interface
:
return
set
(
dst
.
Elem
(),
src
)
case
dstType
.
Kind
()
==
reflect
.
Ptr
&&
dstType
.
Elem
()
!=
derefbigT
:
return
set
(
dst
.
Elem
(),
src
)
case
srcType
.
AssignableTo
(
dstType
)
&&
dst
.
CanSet
()
:
dst
.
Set
(
src
)
case
dstType
.
Kind
()
==
reflect
.
Ptr
:
return
set
(
dst
.
Elem
(),
src
,
output
)
case
dstType
.
Kind
()
==
reflect
.
Slice
&&
srcType
.
Kind
()
==
reflect
.
Slice
:
return
set
Slice
(
dst
,
src
)
default
:
return
fmt
.
Errorf
(
"abi: cannot unmarshal %v in to %v"
,
src
.
Type
(),
dst
.
Type
())
}
...
...
@@ -91,7 +90,7 @@ func set(dst, src reflect.Value, output Argument) error {
// setSlice attempts to assign src to dst when slices are not assignable by default
// e.g. src: [][]byte -> dst: [][15]byte
func
setSlice
(
dst
,
src
reflect
.
Value
,
output
Argument
)
error
{
func
setSlice
(
dst
,
src
reflect
.
Value
)
error
{
slice
:=
reflect
.
MakeSlice
(
dst
.
Type
(),
src
.
Len
(),
src
.
Len
())
for
i
:=
0
;
i
<
src
.
Len
();
i
++
{
v
:=
src
.
Index
(
i
)
...
...
@@ -127,14 +126,14 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
return
nil
}
// mapA
biToStringField maps abi
to struct fields.
// mapA
rgNamesToStructFields maps a slice of argument names
to struct fields.
// first round: for each Exportable field that contains a `abi:""` tag
// and this field name exists in the
arguments
, pair them together.
// second round: for each argument
field
that has not been already linked,
// and this field name exists in the
given argument name list
, pair them together.
// second round: for each argument
name
that has not been already linked,
// find what variable is expected to be mapped into, if it exists and has not been
// used, pair them.
func
mapAbiToStructFields
(
args
Arguments
,
value
reflect
.
Value
)
(
map
[
string
]
string
,
error
)
{
// Note this function assumes the given value is a struct value.
func
mapArgNamesToStructFields
(
argNames
[]
string
,
value
reflect
.
Value
)
(
map
[
string
]
string
,
error
)
{
typ
:=
value
.
Type
()
abi2struct
:=
make
(
map
[
string
]
string
)
...
...
@@ -148,45 +147,39 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
if
structFieldName
[
:
1
]
!=
strings
.
ToUpper
(
structFieldName
[
:
1
])
{
continue
}
// skip fields that have no abi:"" tag.
var
ok
bool
var
tagName
string
if
tagName
,
ok
=
typ
.
Field
(
i
)
.
Tag
.
Lookup
(
"abi"
);
!
ok
{
continue
}
// check if tag is empty.
if
tagName
==
""
{
return
nil
,
fmt
.
Errorf
(
"struct: abi tag in '%s' is empty"
,
structFieldName
)
}
// check which argument field matches with the abi tag.
found
:=
false
for
_
,
a
biField
:=
range
args
.
NonIndexed
()
{
if
a
biField
.
Name
==
tagName
{
if
abi2struct
[
a
biField
.
Name
]
!=
""
{
for
_
,
a
rg
:=
range
argNames
{
if
a
rg
==
tagName
{
if
abi2struct
[
a
rg
]
!=
""
{
return
nil
,
fmt
.
Errorf
(
"struct: abi tag in '%s' already mapped"
,
structFieldName
)
}
// pair them
abi2struct
[
a
biField
.
Name
]
=
structFieldName
struct2abi
[
structFieldName
]
=
a
biField
.
Name
abi2struct
[
a
rg
]
=
structFieldName
struct2abi
[
structFieldName
]
=
a
rg
found
=
true
}
}
// check if this tag has been mapped.
if
!
found
{
return
nil
,
fmt
.
Errorf
(
"struct: abi tag '%s' defined but not found in abi"
,
tagName
)
}
}
// second round ~~~
for
_
,
arg
:=
range
arg
s
{
for
_
,
arg
Name
:=
range
argName
s
{
abiFieldName
:=
arg
.
Name
structFieldName
:=
ToCamelCase
(
abiFieldName
)
structFieldName
:=
ToCamelCase
(
argName
)
if
structFieldName
==
""
{
return
nil
,
fmt
.
Errorf
(
"abi: purely underscored output cannot unpack to struct"
)
...
...
@@ -196,11 +189,11 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
// struct field with the same field name. If so, raise an error:
// abi: [ { "name": "value" } ]
// struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
if
abi2struct
[
a
biField
Name
]
!=
""
{
if
abi2struct
[
a
biField
Name
]
!=
structFieldName
&&
if
abi2struct
[
a
rg
Name
]
!=
""
{
if
abi2struct
[
a
rg
Name
]
!=
structFieldName
&&
struct2abi
[
structFieldName
]
==
""
&&
value
.
FieldByName
(
structFieldName
)
.
IsValid
()
{
return
nil
,
fmt
.
Errorf
(
"abi: multiple variables maps to the same abi field '%s'"
,
a
biField
Name
)
return
nil
,
fmt
.
Errorf
(
"abi: multiple variables maps to the same abi field '%s'"
,
a
rg
Name
)
}
continue
}
...
...
@@ -212,16 +205,14 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
if
value
.
FieldByName
(
structFieldName
)
.
IsValid
()
{
// pair them
abi2struct
[
a
biField
Name
]
=
structFieldName
struct2abi
[
structFieldName
]
=
a
biField
Name
abi2struct
[
a
rg
Name
]
=
structFieldName
struct2abi
[
structFieldName
]
=
a
rg
Name
}
else
{
// not paired, but annotate as used, to detect cases like
// abi : [ { "name": "value" }, { "name": "_value" } ]
// struct { Value *big.Int }
struct2abi
[
structFieldName
]
=
a
biField
Name
struct2abi
[
structFieldName
]
=
a
rg
Name
}
}
return
abi2struct
,
nil
}
accounts/abi/type.go
View file @
7ca40306
...
...
@@ -17,6 +17,7 @@
package
abi
import
(
"errors"
"fmt"
"reflect"
"regexp"
...
...
@@ -32,6 +33,7 @@ const (
StringTy
SliceTy
ArrayTy
TupleTy
AddressTy
FixedBytesTy
BytesTy
...
...
@@ -43,13 +45,16 @@ const (
// Type is the reflection of the supported argument type
type
Type
struct
{
Elem
*
Type
Kind
reflect
.
Kind
Type
reflect
.
Type
Size
int
T
byte
// Our own type checking
stringKind
string
// holds the unparsed string for deriving signatures
// Tuple relative fields
TupleElems
[]
*
Type
// Type information of all tuple fields
TupleRawNames
[]
string
// Raw field name of all tuple fields
}
var
(
...
...
@@ -58,7 +63,7 @@ var (
)
// NewType creates a new reflection type of abi type given in t.
func
NewType
(
t
string
)
(
typ
Type
,
err
error
)
{
func
NewType
(
t
string
,
components
[]
ArgumentMarshaling
)
(
typ
Type
,
err
error
)
{
// check that array brackets are equal if they exist
if
strings
.
Count
(
t
,
"["
)
!=
strings
.
Count
(
t
,
"]"
)
{
return
Type
{},
fmt
.
Errorf
(
"invalid arg type in abi"
)
...
...
@@ -71,7 +76,7 @@ func NewType(t string) (typ Type, err error) {
if
strings
.
Count
(
t
,
"["
)
!=
0
{
i
:=
strings
.
LastIndex
(
t
,
"["
)
// recursively embed the type
embeddedType
,
err
:=
NewType
(
t
[
:
i
])
embeddedType
,
err
:=
NewType
(
t
[
:
i
]
,
components
)
if
err
!=
nil
{
return
Type
{},
err
}
...
...
@@ -87,6 +92,9 @@ func NewType(t string) (typ Type, err error) {
typ
.
Kind
=
reflect
.
Slice
typ
.
Elem
=
&
embeddedType
typ
.
Type
=
reflect
.
SliceOf
(
embeddedType
.
Type
)
if
embeddedType
.
T
==
TupleTy
{
typ
.
stringKind
=
embeddedType
.
stringKind
+
sliced
}
}
else
if
len
(
intz
)
==
1
{
// is a array
typ
.
T
=
ArrayTy
...
...
@@ -97,6 +105,9 @@ func NewType(t string) (typ Type, err error) {
return
Type
{},
fmt
.
Errorf
(
"abi: error parsing variable size: %v"
,
err
)
}
typ
.
Type
=
reflect
.
ArrayOf
(
typ
.
Size
,
embeddedType
.
Type
)
if
embeddedType
.
T
==
TupleTy
{
typ
.
stringKind
=
embeddedType
.
stringKind
+
sliced
}
}
else
{
return
Type
{},
fmt
.
Errorf
(
"invalid formatting of array type"
)
}
...
...
@@ -158,6 +169,40 @@ func NewType(t string) (typ Type, err error) {
typ
.
Size
=
varSize
typ
.
Type
=
reflect
.
ArrayOf
(
varSize
,
reflect
.
TypeOf
(
byte
(
0
)))
}
case
"tuple"
:
var
(
fields
[]
reflect
.
StructField
elems
[]
*
Type
names
[]
string
expression
string
// canonical parameter expression
)
expression
+=
"("
for
idx
,
c
:=
range
components
{
cType
,
err
:=
NewType
(
c
.
Type
,
c
.
Components
)
if
err
!=
nil
{
return
Type
{},
err
}
if
ToCamelCase
(
c
.
Name
)
==
""
{
return
Type
{},
errors
.
New
(
"abi: purely anonymous or underscored field is not supported"
)
}
fields
=
append
(
fields
,
reflect
.
StructField
{
Name
:
ToCamelCase
(
c
.
Name
),
// reflect.StructOf will panic for any exported field.
Type
:
cType
.
Type
,
})
elems
=
append
(
elems
,
&
cType
)
names
=
append
(
names
,
c
.
Name
)
expression
+=
cType
.
stringKind
if
idx
!=
len
(
components
)
-
1
{
expression
+=
","
}
}
expression
+=
")"
typ
.
Kind
=
reflect
.
Struct
typ
.
Type
=
reflect
.
StructOf
(
fields
)
typ
.
TupleElems
=
elems
typ
.
TupleRawNames
=
names
typ
.
T
=
TupleTy
typ
.
stringKind
=
expression
case
"function"
:
typ
.
Kind
=
reflect
.
Array
typ
.
T
=
FunctionTy
...
...
@@ -178,7 +223,6 @@ func (t Type) String() (out string) {
func
(
t
Type
)
pack
(
v
reflect
.
Value
)
([]
byte
,
error
)
{
// dereference pointer first if it's a pointer
v
=
indirect
(
v
)
if
err
:=
typeCheck
(
t
,
v
);
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -196,7 +240,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
offset
:=
0
offsetReq
:=
isDynamicType
(
*
t
.
Elem
)
if
offsetReq
{
offset
=
get
DynamicTypeOffset
(
*
t
.
Elem
)
*
v
.
Len
()
offset
=
get
TypeSize
(
*
t
.
Elem
)
*
v
.
Len
()
}
var
tail
[]
byte
for
i
:=
0
;
i
<
v
.
Len
();
i
++
{
...
...
@@ -213,6 +257,45 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
tail
=
append
(
tail
,
val
...
)
}
return
append
(
ret
,
tail
...
),
nil
case
TupleTy
:
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
// enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
// where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
// type as
// head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
// and as
// head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
// tail(X(i)) = enc(X(i))
// otherwise, i.e. if Ti is a dynamic type.
fieldmap
,
err
:=
mapArgNamesToStructFields
(
t
.
TupleRawNames
,
v
)
if
err
!=
nil
{
return
nil
,
err
}
// Calculate prefix occupied size.
offset
:=
0
for
_
,
elem
:=
range
t
.
TupleElems
{
offset
+=
getTypeSize
(
*
elem
)
}
var
ret
,
tail
[]
byte
for
i
,
elem
:=
range
t
.
TupleElems
{
field
:=
v
.
FieldByName
(
fieldmap
[
t
.
TupleRawNames
[
i
]])
if
!
field
.
IsValid
()
{
return
nil
,
fmt
.
Errorf
(
"field %s for tuple not found in the given struct"
,
t
.
TupleRawNames
[
i
])
}
val
,
err
:=
elem
.
pack
(
field
)
if
err
!=
nil
{
return
nil
,
err
}
if
isDynamicType
(
*
elem
)
{
ret
=
append
(
ret
,
packNum
(
reflect
.
ValueOf
(
offset
))
...
)
tail
=
append
(
tail
,
val
...
)
offset
+=
len
(
val
)
}
else
{
ret
=
append
(
ret
,
val
...
)
}
}
return
append
(
ret
,
tail
...
),
nil
default
:
return
packElement
(
t
,
v
),
nil
}
...
...
@@ -225,25 +308,45 @@ func (t Type) requiresLengthPrefix() bool {
}
// isDynamicType returns true if the type is dynamic.
// StringTy, BytesTy, and SliceTy(irrespective of slice element type) are dynamic types
// ArrayTy is considered dynamic if and only if the Array element is a dynamic type.
// This function recursively checks the type for slice and array elements.
// The following types are called “dynamic”:
// * bytes
// * string
// * T[] for any T
// * T[k] for any dynamic T and any k >= 0
// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
func
isDynamicType
(
t
Type
)
bool
{
// dynamic types
// array is also a dynamic type if the array type is dynamic
if
t
.
T
==
TupleTy
{
for
_
,
elem
:=
range
t
.
TupleElems
{
if
isDynamicType
(
*
elem
)
{
return
true
}
}
return
false
}
return
t
.
T
==
StringTy
||
t
.
T
==
BytesTy
||
t
.
T
==
SliceTy
||
(
t
.
T
==
ArrayTy
&&
isDynamicType
(
*
t
.
Elem
))
}
// getDynamicTypeOffset returns the offset for the type.
// See `isDynamicType` to know which types are considered dynamic.
// If the type t is an array and element type is not a dynamic type, then we consider it a static type and
// return 32 * size of array since length prefix is not required.
// If t is a dynamic type or element type(for slices and arrays) is dynamic, then we simply return 32 as offset.
func
getDynamicTypeOffset
(
t
Type
)
int
{
// if it is an array and there are no dynamic types
// then the array is static type
// getTypeSize returns the size that this type needs to occupy.
// We distinguish static and dynamic types. Static types are encoded in-place
// and dynamic types are encoded at a separately allocated location after the
// current block.
// So for a static variable, the size returned represents the size that the
// variable actually occupies.
// For a dynamic variable, the returned size is fixed 32 bytes, which is used
// to store the location reference for actual value storage.
func
getTypeSize
(
t
Type
)
int
{
if
t
.
T
==
ArrayTy
&&
!
isDynamicType
(
*
t
.
Elem
)
{
return
32
*
t
.
Size
// Recursively calculate type size if it is a nested array
if
t
.
Elem
.
T
==
ArrayTy
{
return
t
.
Size
*
getTypeSize
(
*
t
.
Elem
)
}
return
t
.
Size
*
32
}
else
if
t
.
T
==
TupleTy
&&
!
isDynamicType
(
t
)
{
total
:=
0
for
_
,
elem
:=
range
t
.
TupleElems
{
total
+=
getTypeSize
(
*
elem
)
}
return
total
}
return
32
}
accounts/abi/type_test.go
View file @
7ca40306
This diff is collapsed.
Click to expand it.
accounts/abi/unpack.go
View file @
7ca40306
...
...
@@ -115,17 +115,6 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
}
func
getFullElemSize
(
elem
*
Type
)
int
{
//all other should be counted as 32 (slices have pointers to respective elements)
size
:=
32
//arrays wrap it, each element being the same size
for
elem
.
T
==
ArrayTy
{
size
*=
elem
.
Size
elem
=
elem
.
Elem
}
return
size
}
// iteratively unpack elements
func
forEachUnpack
(
t
Type
,
output
[]
byte
,
start
,
size
int
)
(
interface
{},
error
)
{
if
size
<
0
{
...
...
@@ -150,13 +139,9 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
// Arrays have packed elements, resulting in longer unpack steps.
// Slices have just 32 bytes per element (pointing to the contents).
elemSize
:=
32
if
t
.
T
==
ArrayTy
||
t
.
T
==
SliceTy
{
elemSize
=
getFullElemSize
(
t
.
Elem
)
}
elemSize
:=
getTypeSize
(
*
t
.
Elem
)
for
i
,
j
:=
start
,
0
;
j
<
size
;
i
,
j
=
i
+
elemSize
,
j
+
1
{
inter
,
err
:=
toGoType
(
i
,
*
t
.
Elem
,
output
)
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -170,6 +155,36 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return
refSlice
.
Interface
(),
nil
}
func
forTupleUnpack
(
t
Type
,
output
[]
byte
)
(
interface
{},
error
)
{
retval
:=
reflect
.
New
(
t
.
Type
)
.
Elem
()
virtualArgs
:=
0
for
index
,
elem
:=
range
t
.
TupleElems
{
marshalledValue
,
err
:=
toGoType
((
index
+
virtualArgs
)
*
32
,
*
elem
,
output
)
if
elem
.
T
==
ArrayTy
&&
!
isDynamicType
(
*
elem
)
{
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
// we count the index from now on.
//
// Array values nested multiple levels deep are also encoded inline:
// [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
//
// Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied.
virtualArgs
+=
getTypeSize
(
*
elem
)
/
32
-
1
}
else
if
elem
.
T
==
TupleTy
&&
!
isDynamicType
(
*
elem
)
{
// If we have a static tuple, like (uint256, bool, uint256), these are
// coded as just like uint256,bool,uint256
virtualArgs
+=
getTypeSize
(
*
elem
)
/
32
-
1
}
if
err
!=
nil
{
return
nil
,
err
}
retval
.
Field
(
index
)
.
Set
(
reflect
.
ValueOf
(
marshalledValue
))
}
return
retval
.
Interface
(),
nil
}
// toGoType parses the output bytes and recursively assigns the value of these bytes
// into a go type with accordance with the ABI spec.
func
toGoType
(
index
int
,
t
Type
,
output
[]
byte
)
(
interface
{},
error
)
{
...
...
@@ -178,14 +193,14 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
}
var
(
returnOutput
[]
byte
begin
,
end
int
err
error
returnOutput
[]
byte
begin
,
length
int
err
error
)
// if we require a length prefix, find the beginning word and size returned.
if
t
.
requiresLengthPrefix
()
{
begin
,
end
,
err
=
lengthPrefixPointsTo
(
index
,
output
)
begin
,
length
,
err
=
lengthPrefixPointsTo
(
index
,
output
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -194,19 +209,26 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
}
switch
t
.
T
{
case
SliceTy
:
if
(
*
t
.
Elem
)
.
T
==
StringTy
{
return
forEachUnpack
(
t
,
output
[
begin
:
],
0
,
end
)
case
TupleTy
:
if
isDynamicType
(
t
)
{
begin
,
err
:=
tuplePointsTo
(
index
,
output
)
if
err
!=
nil
{
return
nil
,
err
}
return
forTupleUnpack
(
t
,
output
[
begin
:
])
}
else
{
return
forTupleUnpack
(
t
,
output
[
index
:
])
}
return
forEachUnpack
(
t
,
output
,
begin
,
end
)
case
SliceTy
:
return
forEachUnpack
(
t
,
output
[
begin
:
],
0
,
length
)
case
ArrayTy
:
if
(
*
t
.
Elem
)
.
T
==
StringTy
{
if
isDynamicType
(
*
t
.
Elem
)
{
offset
:=
int64
(
binary
.
BigEndian
.
Uint64
(
returnOutput
[
len
(
returnOutput
)
-
8
:
]))
return
forEachUnpack
(
t
,
output
[
offset
:
],
0
,
t
.
Size
)
}
return
forEachUnpack
(
t
,
output
,
index
,
t
.
Size
)
return
forEachUnpack
(
t
,
output
[
index
:
],
0
,
t
.
Size
)
case
StringTy
:
// variable arrays are written at the end of the return bytes
return
string
(
output
[
begin
:
begin
+
end
]),
nil
return
string
(
output
[
begin
:
begin
+
length
]),
nil
case
IntTy
,
UintTy
:
return
readInteger
(
t
.
T
,
t
.
Kind
,
returnOutput
),
nil
case
BoolTy
:
...
...
@@ -216,7 +238,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
case
HashTy
:
return
common
.
BytesToHash
(
returnOutput
),
nil
case
BytesTy
:
return
output
[
begin
:
begin
+
end
],
nil
return
output
[
begin
:
begin
+
length
],
nil
case
FixedBytesTy
:
return
readFixedBytes
(
t
,
returnOutput
)
case
FunctionTy
:
...
...
@@ -257,3 +279,17 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
length
=
int
(
lengthBig
.
Uint64
())
return
}
// tuplePointsTo resolves the location reference for dynamic tuple.
func
tuplePointsTo
(
index
int
,
output
[]
byte
)
(
start
int
,
err
error
)
{
offset
:=
big
.
NewInt
(
0
)
.
SetBytes
(
output
[
index
:
index
+
32
])
outputLen
:=
big
.
NewInt
(
int64
(
len
(
output
)))
if
offset
.
Cmp
(
big
.
NewInt
(
int64
(
len
(
output
))))
>
0
{
return
0
,
fmt
.
Errorf
(
"abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)"
,
offset
,
outputLen
)
}
if
offset
.
BitLen
()
>
63
{
return
0
,
fmt
.
Errorf
(
"abi offset larger than int64: %v"
,
offset
)
}
return
int
(
offset
.
Uint64
()),
nil
}
accounts/abi/unpack_test.go
View file @
7ca40306
...
...
@@ -173,7 +173,7 @@ var unpackTests = []unpackTest{
// multi dimensional, if these pass, all types that don't require length prefix should pass
{
def
:
`[{"type": "uint8[][]"}]`
,
enc
:
"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000
8000000000000000000000000000000000000000000000000000000000000000E
0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"
,
enc
:
"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000
4000000000000000000000000000000000000000000000000000000000000000a
0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"
,
want
:
[][]
uint8
{{
1
,
2
},
{
1
,
2
}},
},
{
...
...
@@ -183,7 +183,7 @@ var unpackTests = []unpackTest{
},
{
def
:
`[{"type": "uint8[][2]"}]`
,
enc
:
"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"
,
enc
:
"00000000000000000000000000000000000000000000000000000000000000
2000000000000000000000000000000000000000000000000000000000000000
4000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"
,
want
:
[
2
][]
uint8
{{
1
},
{
1
}},
},
{
...
...
@@ -610,7 +610,18 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
t
.
Fatal
(
err
)
}
buff
:=
new
(
bytes
.
Buffer
)
buff
.
Write
(
common
.
Hex2Bytes
(
"000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008657468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000065"
))
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000040"
))
// output[0] offset
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000120"
))
// output[1] offset
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000002"
))
// output[0] length
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000040"
))
// output[0][0] offset
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000080"
))
// output[0][1] offset
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000008"
))
// output[0][0] length
buff
.
Write
(
common
.
Hex2Bytes
(
"657468657265756d000000000000000000000000000000000000000000000000"
))
// output[0][0] value
buff
.
Write
(
common
.
Hex2Bytes
(
"000000000000000000000000000000000000000000000000000000000000000b"
))
// output[0][1] length
buff
.
Write
(
common
.
Hex2Bytes
(
"676f2d657468657265756d000000000000000000000000000000000000000000"
))
// output[0][1] value
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000002"
))
// output[1] length
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000064"
))
// output[1][0] value
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000065"
))
// output[1][1] value
ret1
,
ret1Exp
:=
new
([]
string
),
[]
string
{
"ethereum"
,
"go-ethereum"
}
ret2
,
ret2Exp
:=
new
([]
*
big
.
Int
),
[]
*
big
.
Int
{
big
.
NewInt
(
100
),
big
.
NewInt
(
101
)}
if
err
:=
abi
.
Unpack
(
&
[]
interface
{}{
ret1
,
ret2
},
"multi"
,
buff
.
Bytes
());
err
!=
nil
{
...
...
@@ -913,6 +924,108 @@ func TestUnmarshal(t *testing.T) {
}
}
func
TestUnpackTuple
(
t
*
testing
.
T
)
{
const
simpleTuple
=
`[{"name":"tuple","constant":false,"outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
abi
,
err
:=
JSON
(
strings
.
NewReader
(
simpleTuple
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
buff
:=
new
(
bytes
.
Buffer
)
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
// ret[a] = 1
buff
.
Write
(
common
.
Hex2Bytes
(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
))
// ret[b] = -1
v
:=
struct
{
Ret
struct
{
A
*
big
.
Int
B
*
big
.
Int
}
}{
Ret
:
struct
{
A
*
big
.
Int
B
*
big
.
Int
}{
new
(
big
.
Int
),
new
(
big
.
Int
)}}
err
=
abi
.
Unpack
(
&
v
,
"tuple"
,
buff
.
Bytes
())
if
err
!=
nil
{
t
.
Error
(
err
)
}
else
{
if
v
.
Ret
.
A
.
Cmp
(
big
.
NewInt
(
1
))
!=
0
{
t
.
Errorf
(
"unexpected value unpacked: want %x, got %x"
,
1
,
v
.
Ret
.
A
)
}
if
v
.
Ret
.
B
.
Cmp
(
big
.
NewInt
(
-
1
))
!=
0
{
t
.
Errorf
(
"unexpected value unpacked: want %x, got %x"
,
v
.
Ret
.
B
,
-
1
)
}
}
// Test nested tuple
const
nestedTuple
=
`[{"name":"tuple","constant":false,"outputs":[
{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
{"type":"uint256","name":"a"}
]}]`
abi
,
err
=
JSON
(
strings
.
NewReader
(
nestedTuple
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
buff
.
Reset
()
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000080"
))
// s offset
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000000"
))
// t.X = 0
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
// t.Y = 1
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
// a = 1
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
// s.A = 1
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000060"
))
// s.B offset
buff
.
Write
(
common
.
Hex2Bytes
(
"00000000000000000000000000000000000000000000000000000000000000c0"
))
// s.C offset
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000002"
))
// s.B length
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
// s.B[0] = 1
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000002"
))
// s.B[0] = 2
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000002"
))
// s.C length
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
// s.C[0].X
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000002"
))
// s.C[0].Y
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000002"
))
// s.C[1].X
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
// s.C[1].Y
type
T
struct
{
X
*
big
.
Int
`abi:"x"`
Z
*
big
.
Int
`abi:"y"`
// Test whether the abi tag works.
}
type
S
struct
{
A
*
big
.
Int
B
[]
*
big
.
Int
C
[]
T
}
type
Ret
struct
{
FieldS
S
`abi:"s"`
FieldT
T
`abi:"t"`
A
*
big
.
Int
}
var
ret
Ret
var
expected
=
Ret
{
FieldS
:
S
{
A
:
big
.
NewInt
(
1
),
B
:
[]
*
big
.
Int
{
big
.
NewInt
(
1
),
big
.
NewInt
(
2
)},
C
:
[]
T
{
{
big
.
NewInt
(
1
),
big
.
NewInt
(
2
)},
{
big
.
NewInt
(
2
),
big
.
NewInt
(
1
)},
},
},
FieldT
:
T
{
big
.
NewInt
(
0
),
big
.
NewInt
(
1
),
},
A
:
big
.
NewInt
(
1
),
}
err
=
abi
.
Unpack
(
&
ret
,
"tuple"
,
buff
.
Bytes
())
if
err
!=
nil
{
t
.
Error
(
err
)
}
if
reflect
.
DeepEqual
(
ret
,
expected
)
{
t
.
Error
(
"unexpected unpack value"
)
}
}
func
TestOOMMaliciousInput
(
t
*
testing
.
T
)
{
oomTests
:=
[]
unpackTest
{
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment