aboutsummaryrefslogblamecommitdiffstats
path: root/accounts/abi/type.go
blob: a1f13ffa29f38ecf6cf076074f7ae28f4e2b463c (plain) (tree)
1
2
3
4
5
6
7
8
9
                                         
                                                
  
                                                                                  



                                                                              
                                                                             
                                                                 
                                                               


                                                                           
                                                                                  
 






                 
                 

 
                  



                         
                
               
               
                 

                    
              
                    
                  



                                                        





                                          
 


                                                                              
     
                                             
                                                                            

 
                                                                
                                              


                                                                    
         








                                                                           


                                          























                                                                                                      
                 
                               










                                                                                              
                



                                                                                                   
                 






























                                                                           
                                                

                                                                                    
                 






                                                                        
         



              
                             



                                     


                                                      
 


                                               
 
                                             
                                 
 

                                                           



                                                       
                 
                                   
                                                                   
                                          

                                          
         
                                     
 



                                                                           
                                                                  
 
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package abi

import (
    "fmt"
    "reflect"
    "regexp"
    "strconv"
    "strings"
)

// Type enumerator
const (
    IntTy byte = iota
    UintTy
    BoolTy
    StringTy
    SliceTy
    ArrayTy
    AddressTy
    FixedBytesTy
    BytesTy
    HashTy
    FixedPointTy
    FunctionTy
)

// 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
}

var (
    // typeRegex parses the abi sub types
    typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
)

// NewType creates a new reflection type of abi type given in t.
func NewType(t string) (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")
    }

    typ.stringKind = t

    // if there are brackets, get ready to go into slice/array mode and
    // recursively create the type
    if strings.Count(t, "[") != 0 {
        i := strings.LastIndex(t, "[")
        // recursively embed the type
        embeddedType, err := NewType(t[:i])
        if err != nil {
            return Type{}, err
        }
        // grab the last cell and create a type from there
        sliced := t[i:]
        // grab the slice size with regexp
        re := regexp.MustCompile("[0-9]+")
        intz := re.FindAllString(sliced, -1)

        if len(intz) == 0 {
            // is a slice
            typ.T = SliceTy
            typ.Kind = reflect.Slice
            typ.Elem = &embeddedType
            typ.Type = reflect.SliceOf(embeddedType.Type)
        } else if len(intz) == 1 {
            // is a array
            typ.T = ArrayTy
            typ.Kind = reflect.Array
            typ.Elem = &embeddedType
            typ.Size, err = strconv.Atoi(intz[0])
            if err != nil {
                return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
            }
            typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
        } else {
            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 {
        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
    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.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
}

// String implements Stringer
func (t Type) String() (out string) {
    return t.stringKind
}

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
    }

    if t.T == SliceTy || t.T == ArrayTy {
        var packed []byte

        for i := 0; i < v.Len(); i++ {
            val, err := t.Elem.pack(v.Index(i))
            if err != nil {
                return nil, err
            }
            packed = append(packed, val...)
        }
        if t.T == SliceTy {
            return packBytesSlice(packed, v.Len()), nil
        } else if t.T == ArrayTy {
            return packed, nil
        }
    }
    return packElement(t, v), nil
}

// requireLengthPrefix returns whether the type requires any sort of length
// prefixing.
func (t Type) requiresLengthPrefix() bool {
    return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
}