Implemented contract ABI
This commit is contained in:
190
accounts/abi/type.go
Normal file
190
accounts/abi/type.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
)
|
||||
|
||||
const (
|
||||
IntTy byte = iota
|
||||
UintTy
|
||||
BoolTy
|
||||
SliceTy
|
||||
AddressTy
|
||||
RealTy
|
||||
)
|
||||
|
||||
// Type is the reflection of the supported argument type
|
||||
type Type struct {
|
||||
Kind reflect.Kind
|
||||
Type reflect.Type
|
||||
Size int
|
||||
T byte // Our own type checking
|
||||
stringKind string // holds the unparsed string for deriving signatures
|
||||
}
|
||||
|
||||
// New type returns a fully parsed Type given by the input string or an error if it can't be parsed.
|
||||
//
|
||||
// Strings can be in the format of:
|
||||
//
|
||||
// Input = Type [ "[" [ Number ] "]" ] Name .
|
||||
// Type = [ "u" ] "int" [ Number ] .
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// string int uint real
|
||||
// string32 int8 uint8 uint[]
|
||||
// address int256 uint256 real[2]
|
||||
func NewType(t string) (typ Type, err error) {
|
||||
// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
|
||||
freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
}
|
||||
res := freg.FindAllStringSubmatch(t, -1)[0]
|
||||
var (
|
||||
isslice bool
|
||||
size int
|
||||
)
|
||||
switch {
|
||||
case res[3] != "":
|
||||
// err is ignored. Already checked for number through the regexp
|
||||
size, _ = strconv.Atoi(res[3])
|
||||
isslice = true
|
||||
case res[2] != "":
|
||||
isslice = true
|
||||
size = -1
|
||||
case res[0] == "":
|
||||
return Type{}, fmt.Errorf("type parse error for `%s`", t)
|
||||
}
|
||||
|
||||
treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?")
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
}
|
||||
|
||||
parsedType := treg.FindAllStringSubmatch(res[1], -1)[0]
|
||||
vsize, _ := strconv.Atoi(parsedType[2])
|
||||
vtype := parsedType[1]
|
||||
// substitute canonical representation
|
||||
if vsize == 0 && (vtype == "int" || vtype == "uint") {
|
||||
vsize = 256
|
||||
t += "256"
|
||||
}
|
||||
|
||||
if isslice {
|
||||
typ.Kind = reflect.Slice
|
||||
typ.Size = size
|
||||
switch vtype {
|
||||
case "int":
|
||||
typ.Type = big_ts
|
||||
case "uint":
|
||||
typ.Type = ubig_ts
|
||||
default:
|
||||
return Type{}, fmt.Errorf("unsupported arg slice type: %s", t)
|
||||
}
|
||||
} else {
|
||||
switch vtype {
|
||||
case "int":
|
||||
typ.Kind = reflect.Ptr
|
||||
typ.Type = big_t
|
||||
typ.Size = 256
|
||||
typ.T = IntTy
|
||||
case "uint":
|
||||
typ.Kind = reflect.Ptr
|
||||
typ.Type = ubig_t
|
||||
typ.Size = 256
|
||||
typ.T = UintTy
|
||||
case "bool":
|
||||
typ.Kind = reflect.Bool
|
||||
case "real": // TODO
|
||||
typ.Kind = reflect.Invalid
|
||||
case "address":
|
||||
typ.Kind = reflect.Slice
|
||||
typ.Type = byte_ts
|
||||
typ.Size = 20
|
||||
typ.T = AddressTy
|
||||
case "string":
|
||||
typ.Kind = reflect.String
|
||||
typ.Size = -1
|
||||
if vsize > 0 {
|
||||
typ.Size = 32
|
||||
}
|
||||
default:
|
||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||
}
|
||||
}
|
||||
typ.stringKind = t
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (t Type) String() (out string) {
|
||||
return t.stringKind
|
||||
}
|
||||
|
||||
// Test the given input parameter `v` and checks if it matches certain
|
||||
// criteria
|
||||
// * Big integers are checks for ptr types and if the given value is
|
||||
// assignable
|
||||
// * Integer are checked for size
|
||||
// * Strings, addresses and bytes are checks for type and size
|
||||
func (t Type) pack(v interface{}) ([]byte, error) {
|
||||
value := reflect.ValueOf(v)
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if t.Type != ubig_t {
|
||||
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
|
||||
}
|
||||
return packNum(value, t.T), nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if t.Type != ubig_t {
|
||||
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
|
||||
}
|
||||
return packNum(value, t.T), nil
|
||||
case reflect.Ptr:
|
||||
// If the value is a ptr do a assign check (only used by
|
||||
// big.Int for now)
|
||||
if t.Type == ubig_t && value.Type() != ubig_t {
|
||||
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
|
||||
}
|
||||
return packNum(value, t.T), nil
|
||||
case reflect.String:
|
||||
if t.Size > -1 && value.Len() > t.Size {
|
||||
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
|
||||
}
|
||||
return []byte(ethutil.LeftPadString(t.String(), 32)), nil
|
||||
case reflect.Slice:
|
||||
if t.Size > -1 && value.Len() > t.Size {
|
||||
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
|
||||
}
|
||||
|
||||
// Address is a special slice. The slice acts as one rather than a list of elements.
|
||||
if t.T == AddressTy {
|
||||
return ethutil.LeftPadBytes(v.([]byte), 32), nil
|
||||
}
|
||||
|
||||
// Signed / Unsigned check
|
||||
if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) {
|
||||
return nil, fmt.Errorf("slice of incompatible types.")
|
||||
}
|
||||
|
||||
var packed []byte
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
packed = append(packed, packNum(value.Index(i), t.T)...)
|
||||
}
|
||||
return packed, nil
|
||||
case reflect.Bool:
|
||||
if value.Bool() {
|
||||
return ethutil.LeftPadBytes(ethutil.Big1.Bytes(), 32), nil
|
||||
} else {
|
||||
return ethutil.LeftPadBytes(ethutil.Big0.Bytes(), 32), nil
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreached")
|
||||
}
|
Reference in New Issue
Block a user