accounts/abi: Add one-parameter event test case from enriquefynn/unpack_one_arg_event
This commit is contained in:
@ -136,11 +136,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) {
|
||||
for _, method := range abi.Methods {
|
||||
if bytes.Equal(method.Id(), sigdata[:4]) {
|
||||
return &method
|
||||
return &method, nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
|
||||
}
|
||||
|
@ -702,7 +702,11 @@ func TestABI_MethodById(t *testing.T) {
|
||||
}
|
||||
for name, m := range abi.Methods {
|
||||
a := fmt.Sprintf("%v", m)
|
||||
b := fmt.Sprintf("%v", abi.MethodById(m.Id()))
|
||||
m2, err := abi.MethodById(m.Id())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to look up ABI method: %v", err)
|
||||
}
|
||||
b := fmt.Sprintf("%v", m2)
|
||||
if a != b {
|
||||
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id()))
|
||||
}
|
||||
|
@ -67,6 +67,17 @@ func (arguments Arguments) LengthNonIndexed() int {
|
||||
return out
|
||||
}
|
||||
|
||||
// NonIndexed returns the arguments with indexed arguments filtered out
|
||||
func (arguments Arguments) NonIndexed() Arguments {
|
||||
var ret []Argument
|
||||
for _, arg := range arguments {
|
||||
if !arg.Indexed {
|
||||
ret = append(ret, arg)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
|
||||
func (arguments Arguments) isTuple() bool {
|
||||
return len(arguments) > 1
|
||||
@ -74,21 +85,25 @@ func (arguments Arguments) isTuple() bool {
|
||||
|
||||
// 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() {
|
||||
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||
}
|
||||
marshalledValues, err := arguments.UnpackValues(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if arguments.isTuple() {
|
||||
return arguments.unpackTuple(v, marshalledValues)
|
||||
}
|
||||
return arguments.unpackAtomic(v, marshalledValues)
|
||||
}
|
||||
|
||||
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
||||
|
||||
var (
|
||||
value = valueOf.Elem()
|
||||
value = reflect.ValueOf(v).Elem()
|
||||
typ = value.Type()
|
||||
kind = value.Kind()
|
||||
)
|
||||
@ -110,30 +125,9 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
|
||||
exists[field] = true
|
||||
}
|
||||
}
|
||||
// `i` counts the nonindexed arguments.
|
||||
// `j` counts the number of complex types.
|
||||
// both `i` and `j` are used to to correctly compute `data` offset.
|
||||
for i, arg := range arguments.NonIndexed() {
|
||||
|
||||
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)
|
||||
reflectValue := reflect.ValueOf(marshalledValues[i])
|
||||
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
@ -166,34 +160,72 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
|
||||
}
|
||||
|
||||
// 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)
|
||||
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))
|
||||
}
|
||||
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)
|
||||
elem := reflect.ValueOf(v).Elem()
|
||||
reflectValue := reflect.ValueOf(marshalledValues[0])
|
||||
return set(elem, reflectValue, arguments.NonIndexed()[0])
|
||||
}
|
||||
|
||||
// Unpack performs the operation Go format -> Hexdata
|
||||
// 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,
|
||||
// without supplying a struct to unpack into. Instead, this method returns a list containing the
|
||||
// values. An atomic argument will be a list with one element.
|
||||
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||
retval := make([]interface{}, 0, arguments.LengthNonIndexed())
|
||||
virtualArgs := 0
|
||||
for index, arg := range arguments.NonIndexed() {
|
||||
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
|
||||
// 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 += getArraySize(&arg.Type) - 1
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retval = append(retval, marshalledValue)
|
||||
}
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
// PackValues performs the operation Go format -> Hexdata
|
||||
// It is the semantic opposite of UnpackValues
|
||||
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
|
||||
return arguments.Pack(args...)
|
||||
}
|
||||
|
||||
// Pack 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
|
||||
@ -207,7 +239,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||
inputOffset += 32
|
||||
}
|
||||
}
|
||||
|
||||
var ret []byte
|
||||
for i, a := range args {
|
||||
input := abiArgs[i]
|
||||
@ -216,7 +247,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check for a slice type (string, bytes, slice)
|
||||
if input.Type.requiresLengthPrefix() {
|
||||
// calculate the offset
|
||||
|
@ -428,10 +428,23 @@ func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumb
|
||||
}
|
||||
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
|
||||
receipts := core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash))
|
||||
if receipts == nil {
|
||||
return nil, nil
|
||||
}
|
||||
logs := make([][]*types.Log, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
logs[i] = receipt.Logs
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
<-quit
|
||||
|
@ -164,118 +164,147 @@ var bindType = map[Lang]func(kind abi.Type) string{
|
||||
LangJava: bindTypeJava,
|
||||
}
|
||||
|
||||
// Helper function for the binding generators.
|
||||
// It reads the unmatched characters after the inner type-match,
|
||||
// (since the inner type is a prefix of the total type declaration),
|
||||
// looks for valid arrays (possibly a dynamic one) wrapping the inner type,
|
||||
// and returns the sizes of these arrays.
|
||||
//
|
||||
// Returned array sizes are in the same order as solidity signatures; inner array size first.
|
||||
// Array sizes may also be "", indicating a dynamic array.
|
||||
func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) {
|
||||
remainder := stringKind[innerLen:]
|
||||
//find all the sizes
|
||||
matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1)
|
||||
parts := make([]string, 0, len(matches))
|
||||
for _, match := range matches {
|
||||
//get group 1 from the regex match
|
||||
parts = append(parts, match[1])
|
||||
}
|
||||
return innerMapping, parts
|
||||
}
|
||||
|
||||
// Translates the array sizes to a Go-lang declaration of a (nested) array of the inner type.
|
||||
// Simply returns the inner type if arraySizes is empty.
|
||||
func arrayBindingGo(inner string, arraySizes []string) string {
|
||||
out := ""
|
||||
//prepend all array sizes, from outer (end arraySizes) to inner (start arraySizes)
|
||||
for i := len(arraySizes) - 1; i >= 0; i-- {
|
||||
out += "[" + arraySizes[i] + "]"
|
||||
}
|
||||
out += inner
|
||||
return out
|
||||
}
|
||||
|
||||
// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
|
||||
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
|
||||
// mapped will use an upscaled type (e.g. *big.Int).
|
||||
func bindTypeGo(kind abi.Type) string {
|
||||
stringKind := kind.String()
|
||||
innerLen, innerMapping := bindUnnestedTypeGo(stringKind)
|
||||
return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping))
|
||||
}
|
||||
|
||||
// The inner function of bindTypeGo, this finds the inner type of stringKind.
|
||||
// (Or just the type itself if it is not an array or slice)
|
||||
// The length of the matched part is returned, with the the translated type.
|
||||
func bindUnnestedTypeGo(stringKind string) (int, string) {
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(stringKind, "address"):
|
||||
parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 2 {
|
||||
return stringKind
|
||||
}
|
||||
return fmt.Sprintf("%scommon.Address", parts[1])
|
||||
return len("address"), "common.Address"
|
||||
|
||||
case strings.HasPrefix(stringKind, "bytes"):
|
||||
parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 3 {
|
||||
return stringKind
|
||||
}
|
||||
return fmt.Sprintf("%s[%s]byte", parts[2], parts[1])
|
||||
parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
|
||||
return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1])
|
||||
|
||||
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
|
||||
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 4 {
|
||||
return stringKind
|
||||
}
|
||||
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
|
||||
switch parts[2] {
|
||||
case "8", "16", "32", "64":
|
||||
return fmt.Sprintf("%s%sint%s", parts[3], parts[1], parts[2])
|
||||
return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2])
|
||||
}
|
||||
return fmt.Sprintf("%s*big.Int", parts[3])
|
||||
return len(parts[0]), "*big.Int"
|
||||
|
||||
case strings.HasPrefix(stringKind, "bool") || strings.HasPrefix(stringKind, "string"):
|
||||
parts := regexp.MustCompile(`([a-z]+)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 3 {
|
||||
return stringKind
|
||||
}
|
||||
return fmt.Sprintf("%s%s", parts[2], parts[1])
|
||||
case strings.HasPrefix(stringKind, "bool"):
|
||||
return len("bool"), "bool"
|
||||
|
||||
case strings.HasPrefix(stringKind, "string"):
|
||||
return len("string"), "string"
|
||||
|
||||
default:
|
||||
return stringKind
|
||||
return len(stringKind), stringKind
|
||||
}
|
||||
}
|
||||
|
||||
// Translates the array sizes to a Java declaration of a (nested) array of the inner type.
|
||||
// Simply returns the inner type if arraySizes is empty.
|
||||
func arrayBindingJava(inner string, arraySizes []string) string {
|
||||
// Java array type declarations do not include the length.
|
||||
return inner + strings.Repeat("[]", len(arraySizes))
|
||||
}
|
||||
|
||||
// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
|
||||
// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
|
||||
// mapped will use an upscaled type (e.g. BigDecimal).
|
||||
func bindTypeJava(kind abi.Type) string {
|
||||
stringKind := kind.String()
|
||||
innerLen, innerMapping := bindUnnestedTypeJava(stringKind)
|
||||
return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping))
|
||||
}
|
||||
|
||||
// The inner function of bindTypeJava, this finds the inner type of stringKind.
|
||||
// (Or just the type itself if it is not an array or slice)
|
||||
// The length of the matched part is returned, with the the translated type.
|
||||
func bindUnnestedTypeJava(stringKind string) (int, string) {
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(stringKind, "address"):
|
||||
parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 2 {
|
||||
return stringKind
|
||||
return len(stringKind), stringKind
|
||||
}
|
||||
if parts[1] == "" {
|
||||
return fmt.Sprintf("Address")
|
||||
return len("address"), "Address"
|
||||
}
|
||||
return fmt.Sprintf("Addresses")
|
||||
return len(parts[0]), "Addresses"
|
||||
|
||||
case strings.HasPrefix(stringKind, "bytes"):
|
||||
parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 3 {
|
||||
return stringKind
|
||||
parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 2 {
|
||||
return len(stringKind), stringKind
|
||||
}
|
||||
if parts[2] != "" {
|
||||
return "byte[][]"
|
||||
}
|
||||
return "byte[]"
|
||||
return len(parts[0]), "byte[]"
|
||||
|
||||
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
|
||||
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 4 {
|
||||
return stringKind
|
||||
//Note that uint and int (without digits) are also matched,
|
||||
// these are size 256, and will translate to BigInt (the default).
|
||||
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 3 {
|
||||
return len(stringKind), stringKind
|
||||
}
|
||||
switch parts[2] {
|
||||
case "8", "16", "32", "64":
|
||||
if parts[1] == "" {
|
||||
if parts[3] == "" {
|
||||
return fmt.Sprintf("int%s", parts[2])
|
||||
}
|
||||
return fmt.Sprintf("int%s[]", parts[2])
|
||||
}
|
||||
|
||||
namedSize := map[string]string{
|
||||
"8": "byte",
|
||||
"16": "short",
|
||||
"32": "int",
|
||||
"64": "long",
|
||||
}[parts[2]]
|
||||
|
||||
//default to BigInt
|
||||
if namedSize == "" {
|
||||
namedSize = "BigInt"
|
||||
}
|
||||
if parts[3] == "" {
|
||||
return fmt.Sprintf("BigInt")
|
||||
}
|
||||
return fmt.Sprintf("BigInts")
|
||||
return len(parts[0]), namedSize
|
||||
|
||||
case strings.HasPrefix(stringKind, "bool"):
|
||||
parts := regexp.MustCompile(`bool(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 2 {
|
||||
return stringKind
|
||||
}
|
||||
if parts[1] == "" {
|
||||
return fmt.Sprintf("bool")
|
||||
}
|
||||
return fmt.Sprintf("bool[]")
|
||||
return len("bool"), "boolean"
|
||||
|
||||
case strings.HasPrefix(stringKind, "string"):
|
||||
parts := regexp.MustCompile(`string(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
|
||||
if len(parts) != 2 {
|
||||
return stringKind
|
||||
}
|
||||
if parts[1] == "" {
|
||||
return fmt.Sprintf("String")
|
||||
}
|
||||
return fmt.Sprintf("String[]")
|
||||
return len("string"), "String"
|
||||
|
||||
default:
|
||||
return stringKind
|
||||
return len(stringKind), stringKind
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,11 +354,13 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
|
||||
return "String"
|
||||
case "string[]":
|
||||
return "Strings"
|
||||
case "bool":
|
||||
case "boolean":
|
||||
return "Bool"
|
||||
case "bool[]":
|
||||
case "boolean[]":
|
||||
return "Bools"
|
||||
case "BigInt":
|
||||
case "BigInt[]":
|
||||
return "BigInts"
|
||||
default:
|
||||
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
|
||||
if len(parts) != 4 {
|
||||
return javaKind
|
||||
@ -344,8 +375,6 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
|
||||
default:
|
||||
return javaKind
|
||||
}
|
||||
default:
|
||||
return javaKind
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -737,6 +737,72 @@ var bindTests = []struct {
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
`DeeplyNestedArray`,
|
||||
`
|
||||
contract DeeplyNestedArray {
|
||||
uint64[3][4][5] public deepUint64Array;
|
||||
function storeDeepUintArray(uint64[3][4][5] arr) public {
|
||||
deepUint64Array = arr;
|
||||
}
|
||||
function retrieveDeepArray() public view returns (uint64[3][4][5]) {
|
||||
return deepUint64Array;
|
||||
}
|
||||
}
|
||||
`,
|
||||
`6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029`,
|
||||
`[{"constant":false,"inputs":[{"name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"retrieveDeepArray","outputs":[{"name":"","type":"uint64[3][4][5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"deepUint64Array","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]`,
|
||||
`
|
||||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth := bind.NewKeyedTransactor(key)
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||
|
||||
//deploy the test contract
|
||||
_, _, testContract, err := DeployDeeplyNestedArray(auth, sim)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deploy test contract: %v", err)
|
||||
}
|
||||
|
||||
// Finish deploy.
|
||||
sim.Commit()
|
||||
|
||||
//Create coordinate-filled array, for testing purposes.
|
||||
testArr := [5][4][3]uint64{}
|
||||
for i := 0; i < 5; i++ {
|
||||
testArr[i] = [4][3]uint64{}
|
||||
for j := 0; j < 4; j++ {
|
||||
testArr[i][j] = [3]uint64{}
|
||||
for k := 0; k < 3; k++ {
|
||||
//pack the coordinates, each array value will be unique, and can be validated easily.
|
||||
testArr[i][j][k] = uint64(i) << 16 | uint64(j) << 8 | uint64(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := testContract.StoreDeepUintArray(&bind.TransactOpts{
|
||||
From: auth.From,
|
||||
Signer: auth.Signer,
|
||||
}, testArr); err != nil {
|
||||
t.Fatalf("Failed to store nested array in test contract: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
|
||||
retrievedArr, err := testContract.RetrieveDeepArray(&bind.CallOpts{
|
||||
From: auth.From,
|
||||
Pending: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve nested array from test contract: %v", err)
|
||||
}
|
||||
|
||||
//quick check to see if contents were copied
|
||||
// (See accounts/abi/unpack_test.go for more extensive testing)
|
||||
if retrievedArr[4][3][2] != testArr[4][3][2] {
|
||||
t.Fatalf("Retrieved value does not match expected value! got: %d, expected: %d. %v", retrievedArr[4][3][2], testArr[4][3][2], err)
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
// Tests that packages generated by the binder can be successfully compiled and
|
||||
|
@ -299,6 +299,11 @@ func TestPack(t *testing.T) {
|
||||
[32]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"uint32[2][3][4]",
|
||||
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
|
||||
},
|
||||
{
|
||||
"address[]",
|
||||
[]common.Address{{1}, {2}},
|
||||
|
@ -93,15 +93,28 @@ 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 {
|
||||
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
|
||||
}
|
||||
if start+32*size > len(output) {
|
||||
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
|
||||
}
|
||||
|
||||
// this value will become our slice or our array, depending on the type
|
||||
var refSlice reflect.Value
|
||||
slice := output[start : start+size*32]
|
||||
|
||||
if t.T == SliceTy {
|
||||
// declare our slice
|
||||
@ -113,15 +126,20 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
|
||||
}
|
||||
|
||||
for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 {
|
||||
// this corrects the arrangement so that we get all the underlying array values
|
||||
if t.Elem.T == ArrayTy && j != 0 {
|
||||
i = start + t.Elem.Size*32*j
|
||||
}
|
||||
// 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 {
|
||||
elemSize = getFullElemSize(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
|
||||
}
|
||||
|
||||
// append the item to our reflect slice
|
||||
refSlice.Index(j).Set(reflect.ValueOf(inter))
|
||||
}
|
||||
@ -181,16 +199,32 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||
|
||||
// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
|
||||
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
|
||||
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
|
||||
if offset+32 > len(output) {
|
||||
return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
|
||||
}
|
||||
length = int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
|
||||
if offset+32+length > len(output) {
|
||||
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+length)
|
||||
}
|
||||
start = offset + 32
|
||||
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
|
||||
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
|
||||
outputLength := big.NewInt(int64(len(output)))
|
||||
|
||||
//fmt.Printf("LENGTH PREFIX INFO: \nsize: %v\noffset: %v\nstart: %v\n", length, offset, start)
|
||||
if bigOffsetEnd.Cmp(outputLength) > 0 {
|
||||
return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength)
|
||||
}
|
||||
|
||||
if bigOffsetEnd.BitLen() > 63 {
|
||||
return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd)
|
||||
}
|
||||
|
||||
offsetEnd := int(bigOffsetEnd.Uint64())
|
||||
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
|
||||
|
||||
totalSize := big.NewInt(0)
|
||||
totalSize.Add(totalSize, bigOffsetEnd)
|
||||
totalSize.Add(totalSize, lengthBig)
|
||||
if totalSize.BitLen() > 63 {
|
||||
return 0, 0, fmt.Errorf("abi length larger than int64: %v", totalSize)
|
||||
}
|
||||
|
||||
if totalSize.Cmp(outputLength) > 0 {
|
||||
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
|
||||
}
|
||||
start = int(bigOffsetEnd.Uint64())
|
||||
length = int(lengthBig.Uint64())
|
||||
return
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ var unpackTests = []unpackTest{
|
||||
{
|
||||
def: `[{"type": "bytes32"}]`,
|
||||
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||
want: common.HexToHash("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "function"}]`,
|
||||
@ -189,6 +189,11 @@ var unpackTests = []unpackTest{
|
||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
want: [2]uint32{1, 2},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint32[2][3][4]"}]`,
|
||||
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018",
|
||||
want: [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint64[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
@ -435,6 +440,46 @@ func TestMultiReturnWithArray(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
||||
// Similar to TestMultiReturnWithArray, but with a special case in mind:
|
||||
// values of nested static arrays count towards the size as well, and any element following
|
||||
// after such nested array argument should be read with the correct offset,
|
||||
// so that it does not read content from the previous array argument.
|
||||
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buff := new(bytes.Buffer)
|
||||
// construct the test array, each 3 char element is joined with 61 '0' chars,
|
||||
// to from the ((3 + 61) * 0.5) = 32 byte elements in the array.
|
||||
buff.Write(common.Hex2Bytes(strings.Join([]string{
|
||||
"", //empty, to apply the 61-char separator to the first element as well.
|
||||
"111", "112", "113", "121", "122", "123",
|
||||
"211", "212", "213", "221", "222", "223",
|
||||
"311", "312", "313", "321", "322", "323",
|
||||
"411", "412", "413", "421", "422", "423",
|
||||
}, "0000000000000000000000000000000000000000000000000000000000000")))
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000009876"))
|
||||
|
||||
ret1, ret1Exp := new([4][2][3]uint64), [4][2][3]uint64{
|
||||
{{0x111, 0x112, 0x113}, {0x121, 0x122, 0x123}},
|
||||
{{0x211, 0x212, 0x213}, {0x221, 0x222, 0x223}},
|
||||
{{0x311, 0x312, 0x313}, {0x321, 0x322, 0x323}},
|
||||
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
|
||||
}
|
||||
ret2, ret2Exp := new(uint64), uint64(0x9876)
|
||||
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||
t.Error("array result", *ret1, "!= Expected", ret1Exp)
|
||||
}
|
||||
if *ret2 != ret2Exp {
|
||||
t.Error("int result", *ret2, "!= Expected", ret2Exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
const definition = `[
|
||||
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
|
||||
@ -683,3 +728,73 @@ func TestUnmarshal(t *testing.T) {
|
||||
t.Fatal("expected error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOOMMaliciousInput(t *testing.T) {
|
||||
oomTests := []unpackTest{
|
||||
{
|
||||
def: `[{"type": "uint8[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000003" + // num elems
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
|
||||
},
|
||||
{ // Length larger than 64 bits
|
||||
def: `[{"type": "uint8[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
|
||||
"00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000002" + // num elems
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
|
||||
},
|
||||
{ // Offset very large (over 64 bits)
|
||||
def: `[{"type": "uint8[]"}]`,
|
||||
enc: "00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000020" + // offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
|
||||
},
|
||||
{ // Offset very large (below 64 bits)
|
||||
def: `[{"type": "uint8[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000007ffffffffff00020" + // offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
|
||||
},
|
||||
{ // Offset negative (as 64 bit)
|
||||
def: `[{"type": "uint8[]"}]`,
|
||||
enc: "000000000000000000000000000000000000000000000000f000000000000020" + // offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
|
||||
},
|
||||
|
||||
{ // Negative length
|
||||
def: `[{"type": "uint8[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
|
||||
"000000000000000000000000000000000000000000000000f000000000000002" + // num elems
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
|
||||
},
|
||||
{ // Very large length
|
||||
def: `[{"type": "uint8[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
|
||||
"0000000000000000000000000000000000000000000000007fffffffff000002" + // num elems
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
|
||||
},
|
||||
}
|
||||
for i, test := range oomTests {
|
||||
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
||||
abi, err := JSON(strings.NewReader(def))
|
||||
if err != nil {
|
||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||
}
|
||||
encb, err := hex.DecodeString(test.enc)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid hex: %s" + test.enc)
|
||||
}
|
||||
_, err = abi.Methods["method"].Outputs.UnpackValues(encb)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error on malicious input, test %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user