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
9d187f02
Unverified
Commit
9d187f02
authored
Dec 22, 2017
by
Martin Holst Swende
Committed by
GitHub
Dec 22, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15731 from holiman/revamp_abi
accounts/abi refactor
parents
5f8888e1
c095c87e
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
666 additions
and
379 deletions
+666
-379
abi.go
accounts/abi/abi.go
+28
-24
abi_test.go
accounts/abi/abi_test.go
+59
-4
argument.go
accounts/abi/argument.go
+177
-4
event.go
accounts/abi/event.go
+1
-94
event_test.go
accounts/abi/event_test.go
+237
-0
method.go
accounts/abi/method.go
+16
-146
pack.go
accounts/abi/pack.go
+1
-2
reflect.go
accounts/abi/reflect.go
+25
-0
type.go
accounts/abi/type.go
+56
-58
unpack.go
accounts/abi/unpack.go
+2
-11
unpack_test.go
accounts/abi/unpack_test.go
+64
-36
No files found.
accounts/abi/abi.go
View file @
9d187f02
...
...
@@ -17,6 +17,7 @@
package
abi
import
(
"bytes"
"encoding/json"
"fmt"
"io"
...
...
@@ -50,25 +51,25 @@ func JSON(reader io.Reader) (ABI, error) {
// methods string signature. (signature = baz(uint32,string32))
func
(
abi
ABI
)
Pack
(
name
string
,
args
...
interface
{})
([]
byte
,
error
)
{
// Fetch the ABI of the requested method
var
method
Method
if
name
==
""
{
method
=
abi
.
Constructor
}
else
{
m
,
exist
:=
abi
.
Methods
[
name
]
if
!
exist
{
return
nil
,
fmt
.
Errorf
(
"method '%s' not found"
,
name
)
// constructor
arguments
,
err
:=
abi
.
Constructor
.
Inputs
.
Pack
(
args
...
)
if
err
!=
nil
{
return
nil
,
err
}
method
=
m
return
arguments
,
nil
}
method
,
exist
:=
abi
.
Methods
[
name
]
if
!
exist
{
return
nil
,
fmt
.
Errorf
(
"method '%s' not found"
,
name
)
}
arguments
,
err
:=
method
.
pack
(
args
...
)
arguments
,
err
:=
method
.
Inputs
.
Pack
(
args
...
)
if
err
!=
nil
{
return
nil
,
err
}
// Pack up the method ID too if not a constructor and return
if
name
==
""
{
return
arguments
,
nil
}
return
append
(
method
.
Id
(),
arguments
...
),
nil
}
...
...
@@ -77,28 +78,20 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
if
len
(
output
)
==
0
{
return
fmt
.
Errorf
(
"abi: unmarshalling empty output"
)
}
// since there can't be naming collisions with contracts and events,
// we need to decide whether we're calling a method or an event
var
unpack
unpacker
if
method
,
ok
:=
abi
.
Methods
[
name
];
ok
{
if
len
(
output
)
%
32
!=
0
{
return
fmt
.
Errorf
(
"abi: improperly formatted output"
)
}
unpack
=
method
return
method
.
Outputs
.
Unpack
(
v
,
output
)
}
else
if
event
,
ok
:=
abi
.
Events
[
name
];
ok
{
unpack
=
event
}
else
{
return
fmt
.
Errorf
(
"abi: could not locate named method or event."
)
}
// requires a struct to unpack into for a tuple return...
if
unpack
.
isTupleReturn
()
{
return
unpack
.
tupleUnpack
(
v
,
output
)
return
event
.
Inputs
.
Unpack
(
v
,
output
)
}
return
unpack
.
singleUnpack
(
v
,
output
)
return
fmt
.
Errorf
(
"abi: could not locate named method or event"
)
}
// UnmarshalJSON implements json.Unmarshaler interface
func
(
abi
*
ABI
)
UnmarshalJSON
(
data
[]
byte
)
error
{
var
fields
[]
struct
{
Type
string
...
...
@@ -141,3 +134,14 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
return
nil
}
// MethodById looks up a method by the 4-byte id
// returns nil if none found
func
(
abi
*
ABI
)
MethodById
(
sigdata
[]
byte
)
*
Method
{
for
_
,
method
:=
range
abi
.
Methods
{
if
bytes
.
Equal
(
method
.
Id
(),
sigdata
[
:
4
])
{
return
&
method
}
}
return
nil
}
accounts/abi/abi_test.go
View file @
9d187f02
...
...
@@ -22,10 +22,11 @@ import (
"fmt"
"log"
"math/big"
"reflect"
"strings"
"testing"
"reflect"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
...
...
@@ -75,9 +76,24 @@ func TestReader(t *testing.T) {
}
// deep equal fails for some reason
t
.
Skip
()
if
!
reflect
.
DeepEqual
(
abi
,
exp
)
{
t
.
Errorf
(
"
\n
abi: %v
\n
does not match exp: %v"
,
abi
,
exp
)
for
name
,
expM
:=
range
exp
.
Methods
{
gotM
,
exist
:=
abi
.
Methods
[
name
]
if
!
exist
{
t
.
Errorf
(
"Missing expected method %v"
,
name
)
}
if
!
reflect
.
DeepEqual
(
gotM
,
expM
)
{
t
.
Errorf
(
"
\n
Got abi method:
\n
%v
\n
does not match expected method
\n
%v"
,
gotM
,
expM
)
}
}
for
name
,
gotM
:=
range
abi
.
Methods
{
expM
,
exist
:=
exp
.
Methods
[
name
]
if
!
exist
{
t
.
Errorf
(
"Found extra method %v"
,
name
)
}
if
!
reflect
.
DeepEqual
(
gotM
,
expM
)
{
t
.
Errorf
(
"
\n
Got abi method:
\n
%v
\n
does not match expected method
\n
%v"
,
gotM
,
expM
)
}
}
}
...
...
@@ -641,3 +657,42 @@ func TestUnpackEvent(t *testing.T) {
t
.
Logf
(
"len(data): %d; received event: %+v"
,
len
(
data
),
ev
)
}
}
func
TestABI_MethodById
(
t
*
testing
.
T
)
{
const
abiJSON
=
`[
{"type":"function","name":"receive","constant":false,"inputs":[{"name":"memo","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable"},
{"type":"event","name":"received","anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}]},
{"type":"function","name":"fixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"}]},
{"type":"function","name":"fixedArrBytes","constant":true,"inputs":[{"name":"str","type":"bytes"},{"name":"fixedArr","type":"uint256[2]"}]},
{"type":"function","name":"mixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"}]},
{"type":"function","name":"doubleFixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"fixedArr2","type":"uint256[3]"}]},
{"type":"function","name":"multipleMixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"},{"name":"fixedArr2","type":"uint256[3]"}]},
{"type":"function","name":"balance","constant":true},
{"type":"function","name":"send","constant":false,"inputs":[{"name":"amount","type":"uint256"}]},
{"type":"function","name":"test","constant":false,"inputs":[{"name":"number","type":"uint32"}]},
{"type":"function","name":"string","constant":false,"inputs":[{"name":"inputs","type":"string"}]},
{"type":"function","name":"bool","constant":false,"inputs":[{"name":"inputs","type":"bool"}]},
{"type":"function","name":"address","constant":false,"inputs":[{"name":"inputs","type":"address"}]},
{"type":"function","name":"uint64[2]","constant":false,"inputs":[{"name":"inputs","type":"uint64[2]"}]},
{"type":"function","name":"uint64[]","constant":false,"inputs":[{"name":"inputs","type":"uint64[]"}]},
{"type":"function","name":"foo","constant":false,"inputs":[{"name":"inputs","type":"uint32"}]},
{"type":"function","name":"bar","constant":false,"inputs":[{"name":"inputs","type":"uint32"},{"name":"string","type":"uint16"}]},
{"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[]"}]}
]
`
abi
,
err
:=
JSON
(
strings
.
NewReader
(
abiJSON
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
for
name
,
m
:=
range
abi
.
Methods
{
a
:=
fmt
.
Sprintf
(
"%v"
,
m
)
b
:=
fmt
.
Sprintf
(
"%v"
,
abi
.
MethodById
(
m
.
Id
()))
if
a
!=
b
{
t
.
Errorf
(
"Method %v (id %v) not 'findable' by id in ABI"
,
name
,
common
.
ToHex
(
m
.
Id
()))
}
}
}
accounts/abi/argument.go
View file @
9d187f02
...
...
@@ -19,6 +19,8 @@ package abi
import
(
"encoding/json"
"fmt"
"reflect"
"strings"
)
// Argument holds the name of the argument and the corresponding type.
...
...
@@ -29,7 +31,10 @@ type Argument struct {
Indexed
bool
// indexed is only used by events
}
func
(
a
*
Argument
)
UnmarshalJSON
(
data
[]
byte
)
error
{
type
Arguments
[]
Argument
// UnmarshalJSON implements json.Unmarshaler interface
func
(
argument
*
Argument
)
UnmarshalJSON
(
data
[]
byte
)
error
{
var
extarg
struct
{
Name
string
Type
string
...
...
@@ -40,12 +45,180 @@ func (a *Argument) UnmarshalJSON(data []byte) error {
return
fmt
.
Errorf
(
"argument json err: %v"
,
err
)
}
a
.
Type
,
err
=
NewType
(
extarg
.
Type
)
a
rgument
.
Type
,
err
=
NewType
(
extarg
.
Type
)
if
err
!=
nil
{
return
err
}
a
.
Name
=
extarg
.
Name
a
.
Indexed
=
extarg
.
Indexed
a
rgument
.
Name
=
extarg
.
Name
a
rgument
.
Indexed
=
extarg
.
Indexed
return
nil
}
// LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
// can ever have 'indexed' arguments, it should always be false on arguments for method input/output
func
(
arguments
Arguments
)
LengthNonIndexed
()
int
{
out
:=
0
for
_
,
arg
:=
range
arguments
{
if
!
arg
.
Indexed
{
out
++
}
}
return
out
}
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
func
(
arguments
Arguments
)
isTuple
()
bool
{
return
len
(
arguments
)
>
1
}
// Unpack performs the operation hexdata -> Go format
func
(
arguments
Arguments
)
Unpack
(
v
interface
{},
data
[]
byte
)
error
{
if
arguments
.
isTuple
()
{
return
arguments
.
unpackTuple
(
v
,
data
)
}
return
arguments
.
unpackAtomic
(
v
,
data
)
}
func
(
arguments
Arguments
)
unpackTuple
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is arguments pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
var
(
value
=
valueOf
.
Elem
()
typ
=
value
.
Type
()
kind
=
value
.
Kind
()
)
if
err
:=
requireUnpackKind
(
value
,
typ
,
kind
,
arguments
);
err
!=
nil
{
return
err
}
// `i` counts the nonindexed arguments.
// `j` counts the number of complex types.
// both `i` and `j` are used to to correctly compute `data` offset.
i
,
j
:=
-
1
,
0
for
_
,
arg
:=
range
arguments
{
if
arg
.
Indexed
{
// can't read, continue
continue
}
i
++
marshalledValue
,
err
:=
toGoType
((
i
+
j
)
*
32
,
arg
.
Type
,
output
)
if
err
!=
nil
{
return
err
}
if
arg
.
Type
.
T
==
ArrayTy
{
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j
+=
arg
.
Type
.
Size
-
1
}
reflectValue
:=
reflect
.
ValueOf
(
marshalledValue
)
switch
kind
{
case
reflect
.
Struct
:
for
j
:=
0
;
j
<
typ
.
NumField
();
j
++
{
field
:=
typ
.
Field
(
j
)
// TODO read tags: `abi:"fieldName"`
if
field
.
Name
==
strings
.
ToUpper
(
arg
.
Name
[
:
1
])
+
arg
.
Name
[
1
:
]
{
if
err
:=
set
(
value
.
Field
(
j
),
reflectValue
,
arg
);
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
,
reflectValue
);
err
!=
nil
{
return
err
}
if
err
:=
set
(
v
.
Elem
(),
reflectValue
,
arg
);
err
!=
nil
{
return
err
}
default
:
return
fmt
.
Errorf
(
"abi:[2] cannot unmarshal tuple in to %v"
,
typ
)
}
}
return
nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func
(
arguments
Arguments
)
unpackAtomic
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is arguments pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
arg
:=
arguments
[
0
]
if
arg
.
Indexed
{
return
fmt
.
Errorf
(
"abi: attempting to unpack indexed variable into element."
)
}
value
:=
valueOf
.
Elem
()
marshalledValue
,
err
:=
toGoType
(
0
,
arg
.
Type
,
output
)
if
err
!=
nil
{
return
err
}
return
set
(
value
,
reflect
.
ValueOf
(
marshalledValue
),
arg
)
}
// Unpack performs the operation Go format -> Hexdata
func
(
arguments
Arguments
)
Pack
(
args
...
interface
{})
([]
byte
,
error
)
{
// Make sure arguments match up and pack them
abiArgs
:=
arguments
if
len
(
args
)
!=
len
(
abiArgs
)
{
return
nil
,
fmt
.
Errorf
(
"argument count mismatch: %d for %d"
,
len
(
args
),
len
(
abiArgs
))
}
// variable input is the output appended at the end of packed
// output. This is used for strings and bytes types input.
var
variableInput
[]
byte
// input offset is the bytes offset for packed output
inputOffset
:=
0
for
_
,
abiArg
:=
range
abiArgs
{
if
abiArg
.
Type
.
T
==
ArrayTy
{
inputOffset
+=
(
32
*
abiArg
.
Type
.
Size
)
}
else
{
inputOffset
+=
32
}
}
var
ret
[]
byte
for
i
,
a
:=
range
args
{
input
:=
abiArgs
[
i
]
// pack the input
packed
,
err
:=
input
.
Type
.
pack
(
reflect
.
ValueOf
(
a
))
if
err
!=
nil
{
return
nil
,
err
}
// check for a slice type (string, bytes, slice)
if
input
.
Type
.
requiresLengthPrefix
()
{
// calculate the offset
offset
:=
inputOffset
+
len
(
variableInput
)
// set the offset
ret
=
append
(
ret
,
packNum
(
reflect
.
ValueOf
(
offset
))
...
)
// Append the packed output to the variable input. The variable input
// will be appended at the end of the input.
variableInput
=
append
(
variableInput
,
packed
...
)
}
else
{
// append the packed value to the input
ret
=
append
(
ret
,
packed
...
)
}
}
// append the variable input at the end of the packed input
ret
=
append
(
ret
,
variableInput
...
)
return
ret
,
nil
}
accounts/abi/event.go
View file @
9d187f02
...
...
@@ -18,7 +18,6 @@ package abi
import
(
"fmt"
"reflect"
"strings"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -31,7 +30,7 @@ import (
type
Event
struct
{
Name
string
Anonymous
bool
Inputs
[]
Argument
Inputs
Arguments
}
// Id returns the canonical representation of the event's signature used by the
...
...
@@ -45,95 +44,3 @@ func (e Event) Id() common.Hash {
}
return
common
.
BytesToHash
(
crypto
.
Keccak256
([]
byte
(
fmt
.
Sprintf
(
"%v(%v)"
,
e
.
Name
,
strings
.
Join
(
types
,
","
)))))
}
// unpacks an event return tuple into a struct of corresponding go types
//
// Unpacking can be done into a struct or a slice/array.
func
(
e
Event
)
tupleUnpack
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is a pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
var
(
value
=
valueOf
.
Elem
()
typ
=
value
.
Type
()
)
if
value
.
Kind
()
!=
reflect
.
Struct
{
return
fmt
.
Errorf
(
"abi: cannot unmarshal tuple in to %v"
,
typ
)
}
j
:=
0
for
i
:=
0
;
i
<
len
(
e
.
Inputs
);
i
++
{
input
:=
e
.
Inputs
[
i
]
if
input
.
Indexed
{
// can't read, continue
continue
}
marshalledValue
,
err
:=
toGoType
((
i
+
j
)
*
32
,
input
.
Type
,
output
)
if
err
!=
nil
{
return
err
}
if
input
.
Type
.
T
==
ArrayTy
{
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j
+=
input
.
Type
.
Size
-
1
}
reflectValue
:=
reflect
.
ValueOf
(
marshalledValue
)
switch
value
.
Kind
()
{
case
reflect
.
Struct
:
for
j
:=
0
;
j
<
typ
.
NumField
();
j
++
{
field
:=
typ
.
Field
(
j
)
// TODO read tags: `abi:"fieldName"`
if
field
.
Name
==
strings
.
ToUpper
(
e
.
Inputs
[
i
]
.
Name
[
:
1
])
+
e
.
Inputs
[
i
]
.
Name
[
1
:
]
{
if
err
:=
set
(
value
.
Field
(
j
),
reflectValue
,
e
.
Inputs
[
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
(
e
.
Inputs
),
value
.
Len
())
}
v
:=
value
.
Index
(
i
)
if
v
.
Kind
()
!=
reflect
.
Ptr
&&
v
.
Kind
()
!=
reflect
.
Interface
{
return
fmt
.
Errorf
(
"abi: cannot unmarshal %v in to %v"
,
v
.
Type
(),
reflectValue
.
Type
())
}
reflectValue
:=
reflect
.
ValueOf
(
marshalledValue
)
if
err
:=
set
(
v
.
Elem
(),
reflectValue
,
e
.
Inputs
[
i
]);
err
!=
nil
{
return
err
}
default
:
return
fmt
.
Errorf
(
"abi: cannot unmarshal tuple in to %v"
,
typ
)
}
}
return
nil
}
func
(
e
Event
)
isTupleReturn
()
bool
{
return
len
(
e
.
Inputs
)
>
1
}
func
(
e
Event
)
singleUnpack
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is a pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
if
e
.
Inputs
[
0
]
.
Indexed
{
return
fmt
.
Errorf
(
"abi: attempting to unpack indexed variable into element."
)
}
value
:=
valueOf
.
Elem
()
marshalledValue
,
err
:=
toGoType
(
0
,
e
.
Inputs
[
0
]
.
Type
,
output
)
if
err
!=
nil
{
return
err
}
if
err
:=
set
(
value
,
reflect
.
ValueOf
(
marshalledValue
),
e
.
Inputs
[
0
]);
err
!=
nil
{
return
err
}
return
nil
}
accounts/abi/event_test.go
View file @
9d187f02
...
...
@@ -18,15 +18,52 @@ package abi
import
(
"bytes"
"encoding/hex"
"encoding/json"
"math/big"
"reflect"
"strings"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var
jsonEventTransfer
=
[]
byte
(
`{
"anonymous": false,
"inputs": [
{
"indexed": true, "name": "from", "type": "address"
}, {
"indexed": true, "name": "to", "type": "address"
}, {
"indexed": false, "name": "value", "type": "uint256"
}],
"name": "Transfer",
"type": "event"
}`
)
var
jsonEventPledge
=
[]
byte
(
`{
"anonymous": false,
"inputs": [{
"indexed": false, "name": "who", "type": "address"
}, {
"indexed": false, "name": "wad", "type": "uint128"
}, {
"indexed": false, "name": "currency", "type": "bytes3"
}],
"name": "Pledge",
"type": "event"
}`
)
// 1000000
var
transferData1
=
"00000000000000000000000000000000000000000000000000000000000f4240"
// "0x00Ce0d46d924CC8437c806721496599FC3FFA268", 2218516807680, "usd"
var
pledgeData1
=
"00000000000000000000000000ce0d46d924cc8437c806721496599fc3ffa2680000000000000000000000000000000000000000000000000000020489e800007573640000000000000000000000000000000000000000000000000000000000"
func
TestEventId
(
t
*
testing
.
T
)
{
var
table
=
[]
struct
{
definition
string
...
...
@@ -77,3 +114,203 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
require
.
Equal
(
t
,
[
2
]
uint8
{
1
,
2
},
rst
.
Value1
)
require
.
Equal
(
t
,
uint8
(
3
),
rst
.
Value2
)
}
func
TestEventTupleUnpack
(
t
*
testing
.
T
)
{
type
EventTransfer
struct
{
Value
*
big
.
Int
}
type
EventPledge
struct
{
Who
common
.
Address
Wad
*
big
.
Int
Currency
[
3
]
byte
}
type
BadEventPledge
struct
{
Who
string
Wad
int
Currency
[
3
]
byte
}
bigint
:=
new
(
big
.
Int
)
bigintExpected
:=
big
.
NewInt
(
1000000
)
bigintExpected2
:=
big
.
NewInt
(
2218516807680
)
addr
:=
common
.
HexToAddress
(
"0x00Ce0d46d924CC8437c806721496599FC3FFA268"
)
var
testCases
=
[]
struct
{
data
string
dest
interface
{}
expected
interface
{}
jsonLog
[]
byte
error
string
name
string
}{{
transferData1
,
&
EventTransfer
{},
&
EventTransfer
{
Value
:
bigintExpected
},
jsonEventTransfer
,
""
,
"Can unpack ERC20 Transfer event into structure"
,
},
{
transferData1
,
&
[]
interface
{}{
&
bigint
},
&
[]
interface
{}{
&
bigintExpected
},
jsonEventTransfer
,
""
,
"Can unpack ERC20 Transfer event into slice"
,
},
{
pledgeData1
,
&
EventPledge
{},
&
EventPledge
{
addr
,
bigintExpected2
,
[
3
]
byte
{
'u'
,
's'
,
'd'
}},
jsonEventPledge
,
""
,
"Can unpack Pledge event into structure"
,
},
{
pledgeData1
,
&
[]
interface
{}{
&
common
.
Address
{},
&
bigint
,
&
[
3
]
byte
{}},
&
[]
interface
{}{
&
addr
,
&
bigintExpected2
,
&
[
3
]
byte
{
'u'
,
's'
,
'd'
}},
jsonEventPledge
,
""
,
"Can unpack Pledge event into slice"
,
},
{
pledgeData1
,
&
[
3
]
interface
{}{
&
common
.
Address
{},
&
bigint
,
&
[
3
]
byte
{}},
&
[
3
]
interface
{}{
&
addr
,
&
bigintExpected2
,
&
[
3
]
byte
{
'u'
,
's'
,
'd'
}},
jsonEventPledge
,
""
,
"Can unpack Pledge event into an array"
,
},
{
pledgeData1
,
&
[]
interface
{}{
new
(
int
),
0
,
0
},
&
[]
interface
{}{},
jsonEventPledge
,
"abi: cannot unmarshal common.Address in to int"
,
"Can not unpack Pledge event into slice with wrong types"
,
},
{
pledgeData1
,
&
BadEventPledge
{},
&
BadEventPledge
{},
jsonEventPledge
,
"abi: cannot unmarshal common.Address in to string"
,
"Can not unpack Pledge event into struct with wrong filed types"
,
},
{
pledgeData1
,
&
[]
interface
{}{
common
.
Address
{},
new
(
big
.
Int
)},
&
[]
interface
{}{},
jsonEventPledge
,
"abi: insufficient number of elements in the list/array for unpack, want 3, got 2"
,
"Can not unpack Pledge event into too short slice"
,
},
{
pledgeData1
,
new
(
map
[
string
]
interface
{}),
&
[]
interface
{}{},
jsonEventPledge
,
"abi: cannot unmarshal tuple into map[string]interface {}"
,
"Can not unpack Pledge event into map"
,
}}
for
_
,
tc
:=
range
testCases
{
assert
:=
assert
.
New
(
t
)
tc
:=
tc
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
err
:=
unpackTestEventData
(
tc
.
dest
,
tc
.
data
,
tc
.
jsonLog
,
assert
)
if
tc
.
error
==
""
{
assert
.
Nil
(
err
,
"Should be able to unpack event data."
)
assert
.
Equal
(
tc
.
expected
,
tc
.
dest
,
tc
.
name
)
}
else
{
assert
.
EqualError
(
err
,
tc
.
error
)
}
})
}
}
func
unpackTestEventData
(
dest
interface
{},
hexData
string
,
jsonEvent
[]
byte
,
assert
*
assert
.
Assertions
)
error
{
data
,
err
:=
hex
.
DecodeString
(
hexData
)
assert
.
NoError
(
err
,
"Hex data should be a correct hex-string"
)
var
e
Event
assert
.
NoError
(
json
.
Unmarshal
(
jsonEvent
,
&
e
),
"Should be able to unmarshal event ABI"
)
a
:=
ABI
{
Events
:
map
[
string
]
Event
{
"e"
:
e
}}
return
a
.
Unpack
(
dest
,
"e"
,
data
)
}
/*
Taken from
https://github.com/ethereum/go-ethereum/pull/15568
*/
type
testResult
struct
{
Values
[
2
]
*
big
.
Int
Value1
*
big
.
Int
Value2
*
big
.
Int
}
type
testCase
struct
{
definition
string
want
testResult
}
func
(
tc
testCase
)
encoded
(
intType
,
arrayType
Type
)
[]
byte
{
var
b
bytes
.
Buffer
if
tc
.
want
.
Value1
!=
nil
{
val
,
_
:=
intType
.
pack
(
reflect
.
ValueOf
(
tc
.
want
.
Value1
))
b
.
Write
(
val
)
}
if
!
reflect
.
DeepEqual
(
tc
.
want
.
Values
,
[
2
]
*
big
.
Int
{
nil
,
nil
})
{
val
,
_
:=
arrayType
.
pack
(
reflect
.
ValueOf
(
tc
.
want
.
Values
))
b
.
Write
(
val
)
}
if
tc
.
want
.
Value2
!=
nil
{
val
,
_
:=
intType
.
pack
(
reflect
.
ValueOf
(
tc
.
want
.
Value2
))
b
.
Write
(
val
)
}
return
b
.
Bytes
()
}
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
func
TestEventUnpackIndexed
(
t
*
testing
.
T
)
{
definition
:=
`[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
type
testStruct
struct
{
Value1
uint8
Value2
uint8
}
abi
,
err
:=
JSON
(
strings
.
NewReader
(
definition
))
require
.
NoError
(
t
,
err
)
var
b
bytes
.
Buffer
b
.
Write
(
packNum
(
reflect
.
ValueOf
(
uint8
(
8
))))
var
rst
testStruct
require
.
NoError
(
t
,
abi
.
Unpack
(
&
rst
,
"test"
,
b
.
Bytes
()))
require
.
Equal
(
t
,
uint8
(
0
),
rst
.
Value1
)
require
.
Equal
(
t
,
uint8
(
8
),
rst
.
Value2
)
}
// TestEventIndexedWithArrayUnpack verifies that decoder will not overlow when static array is indexed input.
func
TestEventIndexedWithArrayUnpack
(
t
*
testing
.
T
)
{
definition
:=
`[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
type
testStruct
struct
{
Value1
[
2
]
uint8
Value2
string
}
abi
,
err
:=
JSON
(
strings
.
NewReader
(
definition
))
require
.
NoError
(
t
,
err
)
var
b
bytes
.
Buffer
stringOut
:=
"abc"
// number of fields that will be encoded * 32
b
.
Write
(
packNum
(
reflect
.
ValueOf
(
32
)))
b
.
Write
(
packNum
(
reflect
.
ValueOf
(
len
(
stringOut
))))
b
.
Write
(
common
.
RightPadBytes
([]
byte
(
stringOut
),
32
))
var
rst
testStruct
require
.
NoError
(
t
,
abi
.
Unpack
(
&
rst
,
"test"
,
b
.
Bytes
()))
require
.
Equal
(
t
,
[
2
]
uint8
{
0
,
0
},
rst
.
Value1
)
require
.
Equal
(
t
,
stringOut
,
rst
.
Value2
)
}
accounts/abi/method.go
View file @
9d187f02
...
...
@@ -18,13 +18,12 @@ package abi
import
(
"fmt"
"reflect"
"strings"
"github.com/ethereum/go-ethereum/crypto"
)
//
Callable method
given a `Name` and whether the method is a constant.
//
Method represents a callable
given a `Name` and whether the method is a constant.
// If the method is `Const` no transaction needs to be created for this
// particular Method call. It can easily be simulated using a local VM.
// For example a `Balance()` method only needs to retrieve something
...
...
@@ -35,137 +34,8 @@ import (
type
Method
struct
{
Name
string
Const
bool
Inputs
[]
Argument
Outputs
[]
Argument
}
func
(
method
Method
)
pack
(
args
...
interface
{})
([]
byte
,
error
)
{
// Make sure arguments match up and pack them
if
len
(
args
)
!=
len
(
method
.
Inputs
)
{
return
nil
,
fmt
.
Errorf
(
"argument count mismatch: %d for %d"
,
len
(
args
),
len
(
method
.
Inputs
))
}
// variable input is the output appended at the end of packed
// output. This is used for strings and bytes types input.
var
variableInput
[]
byte
// input offset is the bytes offset for packed output
inputOffset
:=
0
for
_
,
input
:=
range
method
.
Inputs
{
if
input
.
Type
.
T
==
ArrayTy
{
inputOffset
+=
(
32
*
input
.
Type
.
Size
)
}
else
{
inputOffset
+=
32
}
}
var
ret
[]
byte
for
i
,
a
:=
range
args
{
input
:=
method
.
Inputs
[
i
]
// pack the input
packed
,
err
:=
input
.
Type
.
pack
(
reflect
.
ValueOf
(
a
))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"`%s` %v"
,
method
.
Name
,
err
)
}
// check for a slice type (string, bytes, slice)
if
input
.
Type
.
requiresLengthPrefix
()
{
// calculate the offset
offset
:=
inputOffset
+
len
(
variableInput
)
// set the offset
ret
=
append
(
ret
,
packNum
(
reflect
.
ValueOf
(
offset
))
...
)
// Append the packed output to the variable input. The variable input
// will be appended at the end of the input.
variableInput
=
append
(
variableInput
,
packed
...
)
}
else
{
// append the packed value to the input
ret
=
append
(
ret
,
packed
...
)
}
}
// append the variable input at the end of the packed input
ret
=
append
(
ret
,
variableInput
...
)
return
ret
,
nil
}
// unpacks a method return tuple into a struct of corresponding go types
//
// Unpacking can be done into a struct or a slice/array.
func
(
method
Method
)
tupleUnpack
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is a pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
var
(
value
=
valueOf
.
Elem
()
typ
=
value
.
Type
()
)
j
:=
0
for
i
:=
0
;
i
<
len
(
method
.
Outputs
);
i
++
{
toUnpack
:=
method
.
Outputs
[
i
]
marshalledValue
,
err
:=
toGoType
((
i
+
j
)
*
32
,
toUnpack
.
Type
,
output
)
if
err
!=
nil
{
return
err
}
if
toUnpack
.
Type
.
T
==
ArrayTy
{
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j
+=
toUnpack
.
Type
.
Size
-
1
}
reflectValue
:=
reflect
.
ValueOf
(
marshalledValue
)
switch
value
.
Kind
()
{
case
reflect
.
Struct
:
for
j
:=
0
;
j
<
typ
.
NumField
();
j
++
{
field
:=
typ
.
Field
(
j
)
// TODO read tags: `abi:"fieldName"`
if
field
.
Name
==
strings
.
ToUpper
(
method
.
Outputs
[
i
]
.
Name
[
:
1
])
+
method
.
Outputs
[
i
]
.
Name
[
1
:
]
{
if
err
:=
set
(
value
.
Field
(
j
),
reflectValue
,
method
.
Outputs
[
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
(
method
.
Outputs
),
value
.
Len
())
}
v
:=
value
.
Index
(
i
)
if
v
.
Kind
()
!=
reflect
.
Ptr
&&
v
.
Kind
()
!=
reflect
.
Interface
{
return
fmt
.
Errorf
(
"abi: cannot unmarshal %v in to %v"
,
v
.
Type
(),
reflectValue
.
Type
())
}
reflectValue
:=
reflect
.
ValueOf
(
marshalledValue
)
if
err
:=
set
(
v
.
Elem
(),
reflectValue
,
method
.
Outputs
[
i
]);
err
!=
nil
{
return
err
}
default
:
return
fmt
.
Errorf
(
"abi: cannot unmarshal tuple in to %v"
,
typ
)
}
}
return
nil
}
func
(
method
Method
)
isTupleReturn
()
bool
{
return
len
(
method
.
Outputs
)
>
1
}
func
(
method
Method
)
singleUnpack
(
v
interface
{},
output
[]
byte
)
error
{
// make sure the passed value is a pointer
valueOf
:=
reflect
.
ValueOf
(
v
)
if
reflect
.
Ptr
!=
valueOf
.
Kind
()
{
return
fmt
.
Errorf
(
"abi: Unpack(non-pointer %T)"
,
v
)
}
value
:=
valueOf
.
Elem
()
marshalledValue
,
err
:=
toGoType
(
0
,
method
.
Outputs
[
0
]
.
Type
,
output
)
if
err
!=
nil
{
return
err
}
if
err
:=
set
(
value
,
reflect
.
ValueOf
(
marshalledValue
),
method
.
Outputs
[
0
]);
err
!=
nil
{
return
err
}
return
nil
Inputs
Arguments
Outputs
Arguments
}
// Sig returns the methods string signature according to the ABI spec.
...
...
@@ -175,35 +45,35 @@ func (method Method) singleUnpack(v interface{}, output []byte) error {
// function foo(uint32 a, int b) = "foo(uint32,int256)"
//
// Please note that "int" is substitute for its canonical representation "int256"
func
(
m
Method
)
Sig
()
string
{
types
:=
make
([]
string
,
len
(
m
.
Inputs
))
func
(
m
ethod
Method
)
Sig
()
string
{
types
:=
make
([]
string
,
len
(
m
ethod
.
Inputs
))
i
:=
0
for
_
,
input
:=
range
m
.
Inputs
{
for
_
,
input
:=
range
m
ethod
.
Inputs
{
types
[
i
]
=
input
.
Type
.
String
()
i
++
}
return
fmt
.
Sprintf
(
"%v(%v)"
,
m
.
Name
,
strings
.
Join
(
types
,
","
))
return
fmt
.
Sprintf
(
"%v(%v)"
,
m
ethod
.
Name
,
strings
.
Join
(
types
,
","
))
}
func
(
m
Method
)
String
()
string
{
inputs
:=
make
([]
string
,
len
(
m
.
Inputs
))
for
i
,
input
:=
range
m
.
Inputs
{
func
(
m
ethod
Method
)
String
()
string
{
inputs
:=
make
([]
string
,
len
(
m
ethod
.
Inputs
))
for
i
,
input
:=
range
m
ethod
.
Inputs
{
inputs
[
i
]
=
fmt
.
Sprintf
(
"%v %v"
,
input
.
Name
,
input
.
Type
)
}
outputs
:=
make
([]
string
,
len
(
m
.
Outputs
))
for
i
,
output
:=
range
m
.
Outputs
{
outputs
:=
make
([]
string
,
len
(
m
ethod
.
Outputs
))
for
i
,
output
:=
range
m
ethod
.
Outputs
{
if
len
(
output
.
Name
)
>
0
{
outputs
[
i
]
=
fmt
.
Sprintf
(
"%v "
,
output
.
Name
)
}
outputs
[
i
]
+=
output
.
Type
.
String
()
}
constant
:=
""
if
m
.
Const
{
if
m
ethod
.
Const
{
constant
=
"constant "
}
return
fmt
.
Sprintf
(
"function %v(%v) %sreturns(%v)"
,
m
.
Name
,
strings
.
Join
(
inputs
,
", "
),
constant
,
strings
.
Join
(
outputs
,
", "
))
return
fmt
.
Sprintf
(
"function %v(%v) %sreturns(%v)"
,
m
ethod
.
Name
,
strings
.
Join
(
inputs
,
", "
),
constant
,
strings
.
Join
(
outputs
,
", "
))
}
func
(
m
Method
)
Id
()
[]
byte
{
return
crypto
.
Keccak256
([]
byte
(
m
.
Sig
()))[
:
4
]
func
(
m
ethod
Method
)
Id
()
[]
byte
{
return
crypto
.
Keccak256
([]
byte
(
m
ethod
.
Sig
()))[
:
4
]
}
accounts/abi/pack.go
View file @
9d187f02
...
...
@@ -48,9 +48,8 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
case
BoolTy
:
if
reflectValue
.
Bool
()
{
return
math
.
PaddedBigBytes
(
common
.
Big1
,
32
)
}
else
{
return
math
.
PaddedBigBytes
(
common
.
Big0
,
32
)
}
return
math
.
PaddedBigBytes
(
common
.
Big0
,
32
)
case
BytesTy
:
if
reflectValue
.
Kind
()
==
reflect
.
Array
{
reflectValue
=
mustArrayToByteSlice
(
reflectValue
)
...
...
accounts/abi/reflect.go
View file @
9d187f02
...
...
@@ -85,3 +85,28 @@ func set(dst, src reflect.Value, output Argument) error {
}
return
nil
}
// requireAssignable assures that `dest` is a pointer and it's not an interface.
func
requireAssignable
(
dst
,
src
reflect
.
Value
)
error
{
if
dst
.
Kind
()
!=
reflect
.
Ptr
&&
dst
.
Kind
()
!=
reflect
.
Interface
{
return
fmt
.
Errorf
(
"abi: cannot unmarshal %v into %v"
,
src
.
Type
(),
dst
.
Type
())
}
return
nil
}
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
func
requireUnpackKind
(
v
reflect
.
Value
,
t
reflect
.
Type
,
k
reflect
.
Kind
,
args
Arguments
)
error
{
switch
k
{
case
reflect
.
Struct
:
case
reflect
.
Slice
,
reflect
.
Array
:
if
minLen
:=
args
.
LengthNonIndexed
();
v
.
Len
()
<
minLen
{
return
fmt
.
Errorf
(
"abi: insufficient number of elements in the list/array for unpack, want %d, got %d"
,
minLen
,
v
.
Len
())
}
default
:
return
fmt
.
Errorf
(
"abi: cannot unmarshal tuple into %v"
,
t
)
}
return
nil
}
accounts/abi/type.go
View file @
9d187f02
...
...
@@ -24,6 +24,7 @@ import (
"strings"
)
// Type enumerator
const
(
IntTy
byte
=
iota
UintTy
...
...
@@ -100,68 +101,65 @@ func NewType(t string) (typ Type, err error) {
return
Type
{},
fmt
.
Errorf
(
"invalid formatting of array type"
)
}
return
typ
,
err
}
// parse the type and size of the abi-type.
parsedType
:=
typeRegex
.
FindAllStringSubmatch
(
t
,
-
1
)[
0
]
// varSize is the size of the variable
var
varSize
int
if
len
(
parsedType
[
3
])
>
0
{
var
err
error
varSize
,
err
=
strconv
.
Atoi
(
parsedType
[
2
])
if
err
!=
nil
{
return
Type
{},
fmt
.
Errorf
(
"abi: error parsing variable size: %v"
,
err
)
}
}
else
{
// parse the type and size of the abi-type.
parsedType
:=
typeRegex
.
FindAllStringSubmatch
(
t
,
-
1
)[
0
]
// varSize is the size of the variable
var
varSize
int
if
len
(
parsedType
[
3
])
>
0
{
var
err
error
varSize
,
err
=
strconv
.
Atoi
(
parsedType
[
2
])
if
err
!=
nil
{
return
Type
{},
fmt
.
Errorf
(
"abi: error parsing variable size: %v"
,
err
)
}
}
else
{
if
parsedType
[
0
]
==
"uint"
||
parsedType
[
0
]
==
"int"
{
// this should fail because it means that there's something wrong with
// the abi type (the compiler should always format it to the size...always)
return
Type
{},
fmt
.
Errorf
(
"unsupported arg type: %s"
,
t
)
}
if
parsedType
[
0
]
==
"uint"
||
parsedType
[
0
]
==
"int"
{
// this should fail because it means that there's something wrong with
// the abi type (the compiler should always format it to the size...always)
return
Type
{},
fmt
.
Errorf
(
"unsupported arg type: %s"
,
t
)
}
// varType is the parsed abi type
varType
:=
parsedType
[
1
]
switch
varType
{
case
"int"
:
typ
.
Kind
,
typ
.
Type
=
reflectIntKindAndType
(
false
,
varSize
)
typ
.
Size
=
varSize
typ
.
T
=
IntTy
case
"uint"
:
typ
.
Kind
,
typ
.
Type
=
reflectIntKindAndType
(
true
,
varSize
)
typ
.
Size
=
varSize
typ
.
T
=
UintTy
case
"bool"
:
typ
.
Kind
=
reflect
.
Bool
typ
.
T
=
BoolTy
typ
.
Type
=
reflect
.
TypeOf
(
bool
(
false
))
case
"address"
:
typ
.
Kind
=
reflect
.
Array
typ
.
Type
=
address_t
typ
.
Size
=
20
typ
.
T
=
AddressTy
case
"string"
:
typ
.
Kind
=
reflect
.
String
typ
.
Type
=
reflect
.
TypeOf
(
""
)
typ
.
T
=
StringTy
case
"bytes"
:
if
varSize
==
0
{
typ
.
T
=
BytesTy
typ
.
Kind
=
reflect
.
Slice
typ
.
Type
=
reflect
.
SliceOf
(
reflect
.
TypeOf
(
byte
(
0
)))
}
else
{
typ
.
T
=
FixedBytesTy
typ
.
Kind
=
reflect
.
Array
typ
.
Size
=
varSize
typ
.
Type
=
reflect
.
ArrayOf
(
varSize
,
reflect
.
TypeOf
(
byte
(
0
)))
}
case
"function"
:
}
// varType is the parsed abi type
switch
varType
:=
parsedType
[
1
];
varType
{
case
"int"
:
typ
.
Kind
,
typ
.
Type
=
reflectIntKindAndType
(
false
,
varSize
)
typ
.
Size
=
varSize
typ
.
T
=
IntTy
case
"uint"
:
typ
.
Kind
,
typ
.
Type
=
reflectIntKindAndType
(
true
,
varSize
)
typ
.
Size
=
varSize
typ
.
T
=
UintTy
case
"bool"
:
typ
.
Kind
=
reflect
.
Bool
typ
.
T
=
BoolTy
typ
.
Type
=
reflect
.
TypeOf
(
bool
(
false
))
case
"address"
:
typ
.
Kind
=
reflect
.
Array
typ
.
Type
=
address_t
typ
.
Size
=
20
typ
.
T
=
AddressTy
case
"string"
:
typ
.
Kind
=
reflect
.
String
typ
.
Type
=
reflect
.
TypeOf
(
""
)
typ
.
T
=
StringTy
case
"bytes"
:
if
varSize
==
0
{
typ
.
T
=
BytesTy
typ
.
Kind
=
reflect
.
Slice
typ
.
Type
=
reflect
.
SliceOf
(
reflect
.
TypeOf
(
byte
(
0
)))
}
else
{
typ
.
T
=
FixedBytesTy
typ
.
Kind
=
reflect
.
Array
typ
.
T
=
FunctionTy
typ
.
Size
=
24
typ
.
Type
=
reflect
.
ArrayOf
(
24
,
reflect
.
TypeOf
(
byte
(
0
)))
default
:
return
Type
{},
fmt
.
Errorf
(
"unsupported arg type: %s"
,
t
)
typ
.
Size
=
varSize
typ
.
Type
=
reflect
.
ArrayOf
(
varSize
,
reflect
.
TypeOf
(
byte
(
0
)))
}
case
"function"
:
typ
.
Kind
=
reflect
.
Array
typ
.
T
=
FunctionTy
typ
.
Size
=
24
typ
.
Type
=
reflect
.
ArrayOf
(
24
,
reflect
.
TypeOf
(
byte
(
0
)))
default
:
return
Type
{},
fmt
.
Errorf
(
"unsupported arg type: %s"
,
t
)
}
return
...
...
accounts/abi/unpack.go
View file @
9d187f02
...
...
@@ -25,15 +25,6 @@ import (
"github.com/ethereum/go-ethereum/common"
)
// unpacker is a utility interface that enables us to have
// abstraction between events and methods and also to properly
// "unpack" them; e.g. events use Inputs, methods use Outputs.
type
unpacker
interface
{
tupleUnpack
(
v
interface
{},
output
[]
byte
)
error
singleUnpack
(
v
interface
{},
output
[]
byte
)
error
isTupleReturn
()
bool
}
// reads the integer based on its kind
func
readInteger
(
kind
reflect
.
Kind
,
b
[]
byte
)
interface
{}
{
switch
kind
{
...
...
@@ -79,7 +70,7 @@ func readBool(word []byte) (bool, error) {
// This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
func
readFunctionType
(
t
Type
,
word
[]
byte
)
(
funcTy
[
24
]
byte
,
err
error
)
{
if
t
.
T
!=
FunctionTy
{
return
[
24
]
byte
{},
fmt
.
Errorf
(
"abi: invalid type in call to make function type byte array
.
"
)
return
[
24
]
byte
{},
fmt
.
Errorf
(
"abi: invalid type in call to make function type byte array"
)
}
if
garbage
:=
binary
.
BigEndian
.
Uint64
(
word
[
24
:
32
]);
garbage
!=
0
{
err
=
fmt
.
Errorf
(
"abi: got improperly encoded function type, got %v"
,
word
)
...
...
@@ -92,7 +83,7 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
// through reflection, creates a fixed array to be read from
func
readFixedBytes
(
t
Type
,
word
[]
byte
)
(
interface
{},
error
)
{
if
t
.
T
!=
FixedBytesTy
{
return
nil
,
fmt
.
Errorf
(
"abi: invalid type in call to make fixed byte array
.
"
)
return
nil
,
fmt
.
Errorf
(
"abi: invalid type in call to make fixed byte array"
)
}
// convert
array
:=
reflect
.
New
(
t
.
Type
)
.
Elem
()
...
...
accounts/abi/unpack_test.go
View file @
9d187f02
...
...
@@ -27,6 +27,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
type
unpackTest
struct
{
...
...
@@ -286,56 +287,83 @@ func TestUnpack(t *testing.T) {
}
}
func
TestMultiReturnWithStruct
(
t
*
testing
.
T
)
{
type
methodMultiOutput
struct
{
Int
*
big
.
Int
String
string
}
func
methodMultiReturn
(
require
*
require
.
Assertions
)
(
ABI
,
[]
byte
,
methodMultiOutput
)
{
const
definition
=
`[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
var
expected
=
methodMultiOutput
{
big
.
NewInt
(
1
),
"hello"
}
abi
,
err
:=
JSON
(
strings
.
NewReader
(
definition
))
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
require
.
NoError
(
err
)
// using buff to make the code readable
buff
:=
new
(
bytes
.
Buffer
)
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000001"
))
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000040"
))
buff
.
Write
(
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000005"
))
stringOut
:=
"hello"
buff
.
Write
(
common
.
RightPadBytes
([]
byte
(
stringOut
),
32
))
var
inter
struct
{
Int
*
big
.
Int
String
string
}
err
=
abi
.
Unpack
(
&
inter
,
"multi"
,
buff
.
Bytes
())
if
err
!=
nil
{
t
.
Error
(
err
)
}
if
inter
.
Int
==
nil
||
inter
.
Int
.
Cmp
(
big
.
NewInt
(
1
))
!=
0
{
t
.
Error
(
"expected Int to be 1 got"
,
inter
.
Int
)
}
if
inter
.
String
!=
stringOut
{
t
.
Error
(
"expected String to be"
,
stringOut
,
"got"
,
inter
.
String
)
}
buff
.
Write
(
common
.
RightPadBytes
([]
byte
(
expected
.
String
),
32
))
return
abi
,
buff
.
Bytes
(),
expected
}
var
reversed
struct
{
func
TestMethodMultiReturn
(
t
*
testing
.
T
)
{
type
reversed
struct
{
String
string
Int
*
big
.
Int
}
err
=
abi
.
Unpack
(
&
reversed
,
"multi"
,
buff
.
Bytes
())
if
err
!=
nil
{
t
.
Error
(
err
)
}
if
reversed
.
Int
==
nil
||
reversed
.
Int
.
Cmp
(
big
.
NewInt
(
1
))
!=
0
{
t
.
Error
(
"expected Int to be 1 got"
,
reversed
.
Int
)
}
if
reversed
.
String
!=
stringOut
{
t
.
Error
(
"expected String to be"
,
stringOut
,
"got"
,
reversed
.
String
)
abi
,
data
,
expected
:=
methodMultiReturn
(
require
.
New
(
t
))
bigint
:=
new
(
big
.
Int
)
var
testCases
=
[]
struct
{
dest
interface
{}
expected
interface
{}
error
string
name
string
}{{
&
methodMultiOutput
{},
&
expected
,
""
,
"Can unpack into structure"
,
},
{
&
reversed
{},
&
reversed
{
expected
.
String
,
expected
.
Int
},
""
,
"Can unpack into reversed structure"
,
},
{
&
[]
interface
{}{
&
bigint
,
new
(
string
)},
&
[]
interface
{}{
&
expected
.
Int
,
&
expected
.
String
},
""
,
"Can unpack into a slice"
,
},
{
&
[
2
]
interface
{}{
&
bigint
,
new
(
string
)},
&
[
2
]
interface
{}{
&
expected
.
Int
,
&
expected
.
String
},
""
,
"Can unpack into an array"
,
},
{
&
[]
interface
{}{
new
(
int
),
new
(
int
)},
&
[]
interface
{}{
&
expected
.
Int
,
&
expected
.
String
},
"abi: cannot unmarshal *big.Int in to int"
,
"Can not unpack into a slice with wrong types"
,
},
{
&
[]
interface
{}{
new
(
int
)},
&
[]
interface
{}{},
"abi: insufficient number of elements in the list/array for unpack, want 2, got 1"
,
"Can not unpack into a slice with wrong types"
,
}}
for
_
,
tc
:=
range
testCases
{
tc
:=
tc
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
require
:=
require
.
New
(
t
)
err
:=
abi
.
Unpack
(
tc
.
dest
,
"multi"
,
data
)
if
tc
.
error
==
""
{
require
.
Nil
(
err
,
"Should be able to unpack method outputs."
)
require
.
Equal
(
tc
.
expected
,
tc
.
dest
)
}
else
{
require
.
EqualError
(
err
,
tc
.
error
)
}
})
}
}
...
...
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